[libvirt] [PATCH 00/15] Rewrite the QEMU monitor handling v2

This is an update of this series: http://www.redhat.com/archives/libvir-list/2009-October/msg00644.html minus the patches DV already ACKd which I've merged. The main change in this series is that I've removed the change which added a RW-lock primitive in the QEMU driver. Instead I've implemented the job condition variable idea I described here http://www.redhat.com/archives/libvir-list/2009-October/msg00815.html The nice thing about this is that its actually possible to start doing timeouts of commands. This series only partially supports timeouts. A long running monitor command itself will not timeout, but if a second API call is made while someone else is using the monitor, this waiting call will timeout after 30 seconds if the monitor was not relased. Daniel

* src/util/pci.c, src/util/pci.h: Make the pciDeviceList struct opaque to callers of the API. Add accessor methods for managing devices in the list * src/qemu/qemu_driver.c: Update to use APIs instead of directly accessing pciDeviceList fields --- src/libvirt_private.syms | 5 +++ src/qemu/qemu_driver.c | 74 +++++++++++++++++++++++++++------------------ src/util/pci.c | 46 +++++++++++++++++++++++++--- src/util/pci.h | 12 ++++--- 4 files changed, 97 insertions(+), 40 deletions(-) diff --git a/src/libvirt_private.syms b/src/libvirt_private.syms index 15d75fd..1e4a3dd 100644 --- a/src/libvirt_private.syms +++ b/src/libvirt_private.syms @@ -356,6 +356,11 @@ pciDeviceListFree; pciDeviceListAdd; pciDeviceListDel; pciDeviceFileIterate; +pciDeviceListCount; +pciDeviceListGet; +pciDeviceListLock; +pciDeviceListUnlock; +pciDeviceListSteal; # qparams.h diff --git a/src/qemu/qemu_driver.c b/src/qemu/qemu_driver.c index 2b8b550..3bbbb6d 100644 --- a/src/qemu/qemu_driver.c +++ b/src/qemu/qemu_driver.c @@ -1367,7 +1367,7 @@ qemuUpdateActivePciHostdevs(struct qemud_driver *driver, virDomainDefPtr def) { pciDeviceList *pcidevs; - int i, ret; + int ret = -1; if (!def->nhostdevs) return 0; @@ -1375,18 +1375,19 @@ qemuUpdateActivePciHostdevs(struct qemud_driver *driver, if (!(pcidevs = qemuGetPciHostDeviceList(NULL, def))) return -1; - ret = 0; - - for (i = 0; i < pcidevs->count; i++) { + while (pciDeviceListCount(pcidevs) > 0) { + pciDevice *dev = pciDeviceListSteal(NULL, pcidevs, 0); if (pciDeviceListAdd(NULL, driver->activePciHostdevs, - pcidevs->devs[i]) < 0) { - ret = -1; - break; + dev) < 0) { + pciFreeDevice(NULL, dev); + goto cleanup; } - pcidevs->devs[i] = NULL; } + ret = 0; + +cleanup: pciDeviceListFree(NULL, pcidevs); return ret; } @@ -1398,6 +1399,7 @@ qemuPrepareHostDevices(virConnectPtr conn, { pciDeviceList *pcidevs; int i; + int ret = -1; if (!def->nhostdevs) return 0; @@ -1417,33 +1419,39 @@ qemuPrepareHostDevices(virConnectPtr conn, * to pci-stub.ko */ - for (i = 0; i < pcidevs->count; i++) - if (pciDeviceGetManaged(pcidevs->devs[i]) && - pciDettachDevice(conn, pcidevs->devs[i]) < 0) - goto error; + for (i = 0; i < pciDeviceListCount(pcidevs); i++) { + pciDevice *dev = pciDeviceListGet(pcidevs, i); + if (pciDeviceGetManaged(dev) && + pciDettachDevice(conn, dev) < 0) + goto cleanup; + } /* Now that all the PCI hostdevs have be dettached, we can safely * reset them */ - for (i = 0; i < pcidevs->count; i++) - if (pciResetDevice(conn, pcidevs->devs[i], + for (i = 0; i < pciDeviceListCount(pcidevs); i++) { + pciDevice *dev = pciDeviceListGet(pcidevs, i); + if (pciResetDevice(conn, dev, driver->activePciHostdevs) < 0) - goto error; + goto cleanup; + } /* Now mark all the devices as active */ - for (i = 0; i < pcidevs->count; i++) { + for (i = 0; i < pciDeviceListCount(pcidevs); i++) { + pciDevice *dev = pciDeviceListGet(pcidevs, i); + pciDeviceListSteal(NULL, pcidevs, dev); if (pciDeviceListAdd(conn, driver->activePciHostdevs, - pcidevs->devs[i]) < 0) - goto error; - pcidevs->devs[i] = NULL; + dev) < 0) { + pciFreeDevice(NULL, dev); + goto cleanup; + } } - pciDeviceListFree(conn, pcidevs); - return 0; + ret = 0; -error: +cleanup: pciDeviceListFree(conn, pcidevs); - return -1; + return ret; } static void @@ -1468,26 +1476,32 @@ qemuDomainReAttachHostDevices(virConnectPtr conn, /* Again 3 loops; mark all devices as inactive before reset * them and reset all the devices before re-attach */ - for (i = 0; i < pcidevs->count; i++) - pciDeviceListDel(conn, driver->activePciHostdevs, pcidevs->devs[i]); + for (i = 0; i < pciDeviceListCount(pcidevs); i++) { + pciDevice *dev = pciDeviceListGet(pcidevs, i); + pciDeviceListDel(conn, driver->activePciHostdevs, dev); + } - for (i = 0; i < pcidevs->count; i++) - if (pciResetDevice(conn, pcidevs->devs[i], + for (i = 0; i < pciDeviceListCount(pcidevs); i++) { + pciDevice *dev = pciDeviceListGet(pcidevs, i); + if (pciResetDevice(conn, dev, driver->activePciHostdevs) < 0) { virErrorPtr err = virGetLastError(); VIR_ERROR(_("Failed to reset PCI device: %s\n"), err ? err->message : ""); virResetError(err); } + } - for (i = 0; i < pcidevs->count; i++) - if (pciDeviceGetManaged(pcidevs->devs[i]) && - pciReAttachDevice(conn, pcidevs->devs[i]) < 0) { + for (i = 0; i < pciDeviceListCount(pcidevs); i++) { + pciDevice *dev = pciDeviceListGet(pcidevs, i); + if (pciDeviceGetManaged(dev) && + pciReAttachDevice(NULL, dev) < 0) { virErrorPtr err = virGetLastError(); VIR_ERROR(_("Failed to re-attach PCI device: %s\n"), err ? err->message : ""); virResetError(err); } + } pciDeviceListFree(conn, pcidevs); } diff --git a/src/util/pci.c b/src/util/pci.c index feaa6e8..1e003c2 100644 --- a/src/util/pci.c +++ b/src/util/pci.c @@ -66,6 +66,12 @@ struct _pciDevice { unsigned managed : 1; }; +struct _pciDeviceList { + unsigned count; + pciDevice **devs; +}; + + /* For virReportOOMError() and virReportSystemError() */ #define VIR_FROM_THIS VIR_FROM_NONE @@ -980,11 +986,30 @@ pciDeviceListAdd(virConnectPtr conn, return 0; } -void -pciDeviceListDel(virConnectPtr conn ATTRIBUTE_UNUSED, - pciDeviceList *list, - pciDevice *dev) +pciDevice * +pciDeviceListGet(pciDeviceList *list, + int idx) +{ + if (idx >= list->count) + return NULL; + if (idx < 0) + return NULL; + + return list->devs[idx]; +} + +int +pciDeviceListCount(pciDeviceList *list) { + return list->count; +} + +pciDevice * +pciDeviceListSteal(virConnectPtr conn ATTRIBUTE_UNUSED, + pciDeviceList *list, + pciDevice *dev) +{ + pciDevice *ret = NULL; int i; for (i = 0; i < list->count; i++) { @@ -994,7 +1019,7 @@ pciDeviceListDel(virConnectPtr conn ATTRIBUTE_UNUSED, list->devs[i]->function != dev->function) continue; - pciFreeDevice(conn, list->devs[i]); + ret = list->devs[i]; if (i != --list->count) memmove(&list->devs[i], @@ -1007,6 +1032,17 @@ pciDeviceListDel(virConnectPtr conn ATTRIBUTE_UNUSED, break; } + return ret; +} + +void +pciDeviceListDel(virConnectPtr conn, + pciDeviceList *list, + pciDevice *dev) +{ + pciDevice *ret = pciDeviceListSteal(conn, list, dev); + if (ret) + pciFreeDevice(conn, ret); } pciDevice * diff --git a/src/util/pci.h b/src/util/pci.h index 047955f..1f0b9d2 100644 --- a/src/util/pci.h +++ b/src/util/pci.h @@ -25,11 +25,7 @@ #include "internal.h" typedef struct _pciDevice pciDevice; - -typedef struct { - unsigned count; - pciDevice **devs; -} pciDeviceList; +typedef struct _pciDeviceList pciDeviceList; pciDevice *pciGetDevice (virConnectPtr conn, unsigned domain, @@ -55,6 +51,12 @@ void pciDeviceListFree (virConnectPtr conn, int pciDeviceListAdd (virConnectPtr conn, pciDeviceList *list, pciDevice *dev); +pciDevice * pciDeviceListGet (pciDeviceList *list, + int idx); +int pciDeviceListCount (pciDeviceList *list); +pciDevice * pciDeviceListSteal (virConnectPtr conn, + pciDeviceList *list, + pciDevice *dev); void pciDeviceListDel (virConnectPtr conn, pciDeviceList *list, pciDevice *dev); -- 1.6.2.5

On Tue, Nov 03, 2009 at 02:49:55PM -0500, Daniel P. Berrange wrote:
* src/util/pci.c, src/util/pci.h: Make the pciDeviceList struct opaque to callers of the API. Add accessor methods for managing devices in the list * src/qemu/qemu_driver.c: Update to use APIs instead of directly accessing pciDeviceList fields
Sounds good !
[...]
@@ -1417,33 +1419,39 @@ qemuPrepareHostDevices(virConnectPtr conn, * to pci-stub.ko */
- for (i = 0; i < pcidevs->count; i++) - if (pciDeviceGetManaged(pcidevs->devs[i]) && - pciDettachDevice(conn, pcidevs->devs[i]) < 0) - goto error; + for (i = 0; i < pciDeviceListCount(pcidevs); i++) { + pciDevice *dev = pciDeviceListGet(pcidevs, i); + if (pciDeviceGetManaged(dev) && + pciDettachDevice(conn, dev) < 0) + goto cleanup; + }
I wasn't sure at first but since pciDeviceGetManaged() only error is to return NULL then okay ACK, looks fine ! Daniel -- Daniel Veillard | libxml Gnome XML XSLT toolkit http://xmlsoft.org/ daniel@veillard.com | Rpmfind RPM search engine http://rpmfind.net/ http://veillard.com/ | virtualization library http://libvirt.org/

* src/util/threads-pthread.c: pthreads APIs do not set errno, instead the return value is the positive errno. Set errno based on the return value in the wrappers --- src/util/threads-pthread.c | 26 +++++++++++++++----------- 1 files changed, 15 insertions(+), 11 deletions(-) diff --git a/src/util/threads-pthread.c b/src/util/threads-pthread.c index 4e00bc5..b3ec06e 100644 --- a/src/util/threads-pthread.c +++ b/src/util/threads-pthread.c @@ -35,8 +35,9 @@ void virThreadOnExit(void) int virMutexInit(virMutexPtr m) { - if (pthread_mutex_init(&m->lock, NULL) != 0) { - errno = EINVAL; + int ret; + if ((ret = pthread_mutex_init(&m->lock, NULL)) != 0) { + errno = ret; return -1; } return 0; @@ -57,11 +58,11 @@ void virMutexUnlock(virMutexPtr m) } - int virCondInit(virCondPtr c) { - if (pthread_cond_init(&c->cond, NULL) != 0) { - errno = EINVAL; + int ret; + if ((ret = pthread_cond_init(&c->cond, NULL)) != 0) { + errno = ret; return -1; } return 0; @@ -69,8 +70,9 @@ int virCondInit(virCondPtr c) int virCondDestroy(virCondPtr c) { - if (pthread_cond_destroy(&c->cond) != 0) { - errno = EINVAL; + int ret; + if ((ret = pthread_cond_destroy(&c->cond)) != 0) { + errno = ret; return -1; } return 0; @@ -78,8 +80,9 @@ int virCondDestroy(virCondPtr c) int virCondWait(virCondPtr c, virMutexPtr m) { - if (pthread_cond_wait(&c->cond, &m->lock) != 0) { - errno = EINVAL; + int ret; + if ((ret = pthread_cond_wait(&c->cond, &m->lock)) != 0) { + errno = ret; return -1; } return 0; @@ -99,8 +102,9 @@ void virCondBroadcast(virCondPtr c) int virThreadLocalInit(virThreadLocalPtr l, virThreadLocalCleanup c) { - if (pthread_key_create(&l->key, c) != 0) { - errno = EINVAL; + int ret; + if ((ret = pthread_key_create(&l->key, c)) != 0) { + errno = ret; return -1; } return 0; -- 1.6.2.5

On Tue, Nov 03, 2009 at 02:49:56PM -0500, Daniel P. Berrange wrote:
* src/util/threads-pthread.c: pthreads APIs do not set errno, instead the return value is the positive errno. Set errno based on the return value in the wrappers --- src/util/threads-pthread.c | 26 +++++++++++++++----------- 1 files changed, 15 insertions(+), 11 deletions(-)
diff --git a/src/util/threads-pthread.c b/src/util/threads-pthread.c index 4e00bc5..b3ec06e 100644 --- a/src/util/threads-pthread.c +++ b/src/util/threads-pthread.c @@ -35,8 +35,9 @@ void virThreadOnExit(void)
int virMutexInit(virMutexPtr m) { - if (pthread_mutex_init(&m->lock, NULL) != 0) { - errno = EINVAL; + int ret; + if ((ret = pthread_mutex_init(&m->lock, NULL)) != 0) { + errno = ret; return -1; } return 0; @@ -57,11 +58,11 @@ void virMutexUnlock(virMutexPtr m) }
- int virCondInit(virCondPtr c) { - if (pthread_cond_init(&c->cond, NULL) != 0) { - errno = EINVAL; + int ret; + if ((ret = pthread_cond_init(&c->cond, NULL)) != 0) { + errno = ret; return -1; } return 0; @@ -69,8 +70,9 @@ int virCondInit(virCondPtr c)
int virCondDestroy(virCondPtr c) { - if (pthread_cond_destroy(&c->cond) != 0) { - errno = EINVAL; + int ret; + if ((ret = pthread_cond_destroy(&c->cond)) != 0) { + errno = ret; return -1; } return 0; @@ -78,8 +80,9 @@ int virCondDestroy(virCondPtr c)
int virCondWait(virCondPtr c, virMutexPtr m) { - if (pthread_cond_wait(&c->cond, &m->lock) != 0) { - errno = EINVAL; + int ret; + if ((ret = pthread_cond_wait(&c->cond, &m->lock)) != 0) { + errno = ret; return -1; } return 0; @@ -99,8 +102,9 @@ void virCondBroadcast(virCondPtr c) int virThreadLocalInit(virThreadLocalPtr l, virThreadLocalCleanup c) { - if (pthread_key_create(&l->key, c) != 0) { - errno = EINVAL; + int ret; + if ((ret = pthread_key_create(&l->key, c)) != 0) { + errno = ret; return -1; } return 0;
ACK, Daniel -- Daniel Veillard | libxml Gnome XML XSLT toolkit http://xmlsoft.org/ daniel@veillard.com | Rpmfind RPM search engine http://rpmfind.net/ http://veillard.com/ | virtualization library http://libvirt.org/

* src/util/threads.h, src/util/threads-pthread.c, src/libvirt_private.syms: Add virCondWaitUntil() --- src/libvirt_private.syms | 1 + src/util/threads-pthread.c | 15 +++++++++++++++ src/util/threads.h | 1 + 3 files changed, 17 insertions(+), 0 deletions(-) diff --git a/src/libvirt_private.syms b/src/libvirt_private.syms index 1e4a3dd..600dfee 100644 --- a/src/libvirt_private.syms +++ b/src/libvirt_private.syms @@ -443,6 +443,7 @@ virMutexUnlock; virCondInit; virCondDestroy; virCondWait; +virCondWaitUntil; virCondSignal; virCondBroadcast; diff --git a/src/util/threads-pthread.c b/src/util/threads-pthread.c index b3ec06e..ad42483 100644 --- a/src/util/threads-pthread.c +++ b/src/util/threads-pthread.c @@ -88,6 +88,21 @@ int virCondWait(virCondPtr c, virMutexPtr m) return 0; } +int virCondWaitUntil(virCondPtr c, virMutexPtr m, unsigned long long whenms) +{ + int ret; + struct timespec ts; + + ts.tv_sec = whenms / 1000; + ts.tv_nsec = (whenms % 1000) * 1000; + + if ((ret = pthread_cond_timedwait(&c->cond, &m->lock, &ts)) != 0) { + errno = ret; + return -1; + } + return 0; +} + void virCondSignal(virCondPtr c) { pthread_cond_signal(&c->cond); diff --git a/src/util/threads.h b/src/util/threads.h index 62239b7..d97463d 100644 --- a/src/util/threads.h +++ b/src/util/threads.h @@ -49,6 +49,7 @@ int virCondInit(virCondPtr c) ATTRIBUTE_RETURN_CHECK; int virCondDestroy(virCondPtr c) ATTRIBUTE_RETURN_CHECK; int virCondWait(virCondPtr c, virMutexPtr m) ATTRIBUTE_RETURN_CHECK; +int virCondWaitUntil(virCondPtr c, virMutexPtr m, unsigned long long whenms) ATTRIBUTE_RETURN_CHECK; void virCondSignal(virCondPtr c); void virCondBroadcast(virCondPtr c); -- 1.6.2.5

On Tue, Nov 03, 2009 at 02:49:57PM -0500, Daniel P. Berrange wrote:
* src/util/threads.h, src/util/threads-pthread.c, src/libvirt_private.syms: Add virCondWaitUntil() --- src/libvirt_private.syms | 1 + src/util/threads-pthread.c | 15 +++++++++++++++ src/util/threads.h | 1 + 3 files changed, 17 insertions(+), 0 deletions(-)
diff --git a/src/libvirt_private.syms b/src/libvirt_private.syms index 1e4a3dd..600dfee 100644 --- a/src/libvirt_private.syms +++ b/src/libvirt_private.syms @@ -443,6 +443,7 @@ virMutexUnlock; virCondInit; virCondDestroy; virCondWait; +virCondWaitUntil; virCondSignal; virCondBroadcast;
diff --git a/src/util/threads-pthread.c b/src/util/threads-pthread.c index b3ec06e..ad42483 100644 --- a/src/util/threads-pthread.c +++ b/src/util/threads-pthread.c @@ -88,6 +88,21 @@ int virCondWait(virCondPtr c, virMutexPtr m) return 0; }
+int virCondWaitUntil(virCondPtr c, virMutexPtr m, unsigned long long whenms) +{ + int ret; + struct timespec ts; + + ts.tv_sec = whenms / 1000; + ts.tv_nsec = (whenms % 1000) * 1000; + + if ((ret = pthread_cond_timedwait(&c->cond, &m->lock, &ts)) != 0) { + errno = ret; + return -1; + } + return 0; +} + void virCondSignal(virCondPtr c) { pthread_cond_signal(&c->cond); diff --git a/src/util/threads.h b/src/util/threads.h index 62239b7..d97463d 100644 --- a/src/util/threads.h +++ b/src/util/threads.h @@ -49,6 +49,7 @@ int virCondInit(virCondPtr c) ATTRIBUTE_RETURN_CHECK; int virCondDestroy(virCondPtr c) ATTRIBUTE_RETURN_CHECK;
int virCondWait(virCondPtr c, virMutexPtr m) ATTRIBUTE_RETURN_CHECK; +int virCondWaitUntil(virCondPtr c, virMutexPtr m, unsigned long long whenms) ATTRIBUTE_RETURN_CHECK; void virCondSignal(virCondPtr c); void virCondBroadcast(virCondPtr c);
Haha, now I understand the slight change and why it's the thread waiting which gets to timeout, simpler, and probably as practical ACK Daniel -- Daniel Veillard | libxml Gnome XML XSLT toolkit http://xmlsoft.org/ daniel@veillard.com | Rpmfind RPM search engine http://rpmfind.net/ http://veillard.com/ | virtualization library http://libvirt.org/

The qemu_driver.c code should not contain any code that interacts with the QEMU monitor at a low level. A previous commit moved all the command invocations out. This change moves out the code which actually opens the monitor device. * src/qemu/qemu_driver.c: Remove qemudOpenMonitor & methods called from it. * src/Makefile.am: Add qemu_monitor.{c,h} * src/qemu/qemu_monitor.h: Add qemuMonitorOpen() * src/qemu/qemu_monitor.c: All code for opening the monitor --- po/POTFILES.in | 1 + src/Makefile.am | 1 + src/qemu/qemu_driver.c | 306 ++++++---------------------------------------- src/qemu/qemu_monitor.c | 268 +++++++++++++++++++++++++++++++++++++++++ src/qemu/qemu_monitor.h | 37 ++++++ 5 files changed, 347 insertions(+), 266 deletions(-) create mode 100644 src/qemu/qemu_monitor.c create mode 100644 src/qemu/qemu_monitor.h diff --git a/po/POTFILES.in b/po/POTFILES.in index 1a12a39..5b9a364 100644 --- a/po/POTFILES.in +++ b/po/POTFILES.in @@ -26,6 +26,7 @@ src/openvz/openvz_driver.c src/phyp/phyp_driver.c src/qemu/qemu_conf.c src/qemu/qemu_driver.c +src/qemu/qemu_monitor.c src/qemu/qemu_monitor_text.c src/remote/remote_driver.c src/secret/secret_driver.c diff --git a/src/Makefile.am b/src/Makefile.am index 8fbfb5f..9d9ab44 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -182,6 +182,7 @@ VBOX_DRIVER_EXTRA_DIST = vbox/vbox_tmpl.c vbox/README QEMU_DRIVER_SOURCES = \ qemu/qemu_conf.c qemu/qemu_conf.h \ + qemu/qemu_monitor.c qemu/qemu_monitor.h \ qemu/qemu_monitor_text.c \ qemu/qemu_monitor_text.h \ qemu/qemu_driver.c qemu/qemu_driver.h diff --git a/src/qemu/qemu_driver.c b/src/qemu/qemu_driver.c index 3bbbb6d..8535fac 100644 --- a/src/qemu/qemu_driver.c +++ b/src/qemu/qemu_driver.c @@ -55,6 +55,7 @@ #include "datatypes.h" #include "qemu_driver.h" #include "qemu_conf.h" +#include "qemu_monitor.h" #include "qemu_monitor_text.h" #include "c-ctype.h" #include "event.h" @@ -290,11 +291,24 @@ qemudRemoveDomainStatus(virConnectPtr conn, return 0; } +static int +qemuConnectMonitor(virDomainObjPtr vm, int reconnect) +{ + int rc; + if ((rc = qemuMonitorOpen(vm, reconnect)) != 0) { + VIR_ERROR(_("Failed to connect monitor for %s: %d\n"), + vm->def->name, rc); + return -1; + } -static int qemudOpenMonitor(virConnectPtr conn, - virDomainObjPtr vm, - int reconnect); + if ((vm->monitorWatch = virEventAddHandle(vm->monitor, + VIR_EVENT_HANDLE_HANGUP | VIR_EVENT_HANDLE_ERROR, + qemudDispatchVMEvent, + vm, NULL)) < 0) + return -1; + return 0; +} /* * Open an existing VM's monitor, re-detect VCPU threads @@ -303,17 +317,13 @@ static int qemudOpenMonitor(virConnectPtr conn, static void qemuReconnectDomain(void *payload, const char *name ATTRIBUTE_UNUSED, void *opaque) { - int rc; virDomainObjPtr obj = payload; struct qemud_driver *driver = opaque; virDomainObjLock(obj); - if ((rc = qemudOpenMonitor(NULL, obj, 1)) != 0) { - VIR_ERROR(_("Failed to reconnect monitor for %s: %d\n"), - obj->def->name, rc); + if (qemuConnectMonitor(obj, 1) < 0) goto error; - } if (qemuUpdateActivePciHostdevs(driver, obj->def) < 0) { goto error; @@ -747,89 +757,10 @@ qemudShutdown(void) { return 0; } -/* Return -1 for error, 1 to continue reading and 0 for success */ -typedef int qemudHandlerMonitorOutput(virConnectPtr conn, - virDomainObjPtr vm, - const char *output, - int fd); - -/* - * Returns -1 for error, 0 on end-of-file, 1 for success - */ -static int -qemudReadMonitorOutput(virConnectPtr conn, - virDomainObjPtr vm, - int fd, - char *buf, - size_t buflen, - qemudHandlerMonitorOutput func, - const char *what, - int timeout) -{ - size_t got = 0; - buf[0] = '\0'; - timeout *= 1000; /* poll wants milli seconds */ - - /* Consume & discard the initial greeting */ - while (got < (buflen-1)) { - ssize_t ret; - - ret = read(fd, buf+got, buflen-got-1); - - if (ret < 0) { - struct pollfd pfd = { .fd = fd, .events = POLLIN }; - if (errno == EINTR) - continue; - - if (errno != EAGAIN) { - virReportSystemError(conn, errno, - _("Failure while reading %s startup output"), - what); - return -1; - } - - ret = poll(&pfd, 1, timeout); - if (ret == 0) { - qemudReportError(conn, NULL, NULL, VIR_ERR_INTERNAL_ERROR, - _("Timed out while reading %s startup output"), what); - return -1; - } else if (ret == -1) { - if (errno != EINTR) { - virReportSystemError(conn, errno, - _("Failure while reading %s startup output"), - what); - return -1; - } - } else { - /* Make sure we continue loop & read any further data - available before dealing with EOF */ - if (pfd.revents & (POLLIN | POLLHUP)) - continue; - - qemudReportError(conn, NULL, NULL, VIR_ERR_INTERNAL_ERROR, - _("Failure while reading %s startup output"), what); - return -1; - } - } else if (ret == 0) { - return 0; - } else { - got += ret; - buf[got] = '\0'; - ret = func(conn, vm, buf, fd); - if (ret == -1) - return -1; - if (ret == 1) - continue; - return 1; - } - } - - qemudReportError(conn, NULL, NULL, VIR_ERR_INTERNAL_ERROR, - _("Out of space while reading %s startup output"), what); - return -1; - -} - +typedef int qemuLogHandleOutput(virConnectPtr conn, + virDomainObjPtr vm, + const char *output, + int fd); /* * Returns -1 for error, 0 on success @@ -840,7 +771,7 @@ qemudReadLogOutput(virConnectPtr conn, int fd, char *buf, size_t buflen, - qemudHandlerMonitorOutput func, + qemuLogHandleOutput func, const char *what, int timeout) { @@ -895,177 +826,20 @@ qemudReadLogOutput(virConnectPtr conn, return -1; } -static int -qemudCheckMonitorPrompt(virConnectPtr conn ATTRIBUTE_UNUSED, - virDomainObjPtr vm, - const char *output, - int fd) -{ - if (strstr(output, "(qemu) ") == NULL) - return 1; /* keep reading */ - - vm->monitor = fd; - - return 0; -} - -static int -qemudOpenMonitorCommon(virConnectPtr conn, - virDomainObjPtr vm, - int monfd, - int reconnect) -{ - char buf[1024]; - int ret; - - if (virSetCloseExec(monfd) < 0) { - qemudReportError(conn, NULL, NULL, VIR_ERR_INTERNAL_ERROR, - "%s", _("Unable to set monitor close-on-exec flag")); - return -1; - } - if (virSetNonBlock(monfd) < 0) { - qemudReportError(conn, NULL, NULL, VIR_ERR_INTERNAL_ERROR, - "%s", _("Unable to put monitor into non-blocking mode")); - return -1; - } - - if (!reconnect) { - if (qemudReadMonitorOutput(conn, - vm, monfd, - buf, sizeof(buf), - qemudCheckMonitorPrompt, - "monitor", 10) <= 0) - ret = -1; - else - ret = 0; - } else { - vm->monitor = monfd; - ret = 0; - } - - if (ret != 0) - return ret; - if ((vm->monitorWatch = virEventAddHandle(vm->monitor, - VIR_EVENT_HANDLE_HANGUP | VIR_EVENT_HANDLE_ERROR, - qemudDispatchVMEvent, - vm, NULL)) < 0) - return -1; - - return 0; -} - -static int -qemudOpenMonitorUnix(virConnectPtr conn, - virDomainObjPtr vm, - const char *monitor, - int reconnect) -{ - struct sockaddr_un addr; - int monfd; - int timeout = 3; /* In seconds */ - int ret, i = 0; - - if ((monfd = socket(AF_UNIX, SOCK_STREAM, 0)) < 0) { - virReportSystemError(conn, errno, - "%s", _("failed to create socket")); - return -1; - } - - memset(&addr, 0, sizeof(addr)); - addr.sun_family = AF_UNIX; - if (virStrcpyStatic(addr.sun_path, monitor) == NULL) { - qemudReportError(conn, NULL, NULL, VIR_ERR_INTERNAL_ERROR, - _("Monitor path %s too big for destination"), monitor); - goto error; - } - - do { - ret = connect(monfd, (struct sockaddr *) &addr, sizeof(addr)); - - if (ret == 0) - break; - - if (errno == ENOENT || errno == ECONNREFUSED) { - /* ENOENT : Socket may not have shown up yet - * ECONNREFUSED : Leftover socket hasn't been removed yet */ - continue; - } - - virReportSystemError(conn, errno, "%s", - _("failed to connect to monitor socket")); - goto error; - - } while ((++i <= timeout*5) && (usleep(.2 * 1000000) <= 0)); - - if (ret != 0) { - virReportSystemError(conn, errno, "%s", - _("monitor socket did not show up.")); - goto error; - } - - if (qemudOpenMonitorCommon(conn, vm, monfd, reconnect) < 0) - goto error; - - return 0; - -error: - close(monfd); - return -1; -} - -static int -qemudOpenMonitorPty(virConnectPtr conn, - virDomainObjPtr vm, - const char *monitor, - int reconnect) -{ - int monfd; - - if ((monfd = open(monitor, O_RDWR)) < 0) { - qemudReportError(conn, NULL, NULL, VIR_ERR_INTERNAL_ERROR, - _("Unable to open monitor path %s"), monitor); - return -1; - } - - if (qemudOpenMonitorCommon(conn, vm, monfd, reconnect) < 0) - goto error; - - return 0; - -error: - close(monfd); - return -1; -} - -static int -qemudOpenMonitor(virConnectPtr conn, - virDomainObjPtr vm, - int reconnect) -{ - switch (vm->monitor_chr->type) { - case VIR_DOMAIN_CHR_TYPE_UNIX: - return qemudOpenMonitorUnix(conn, vm, - vm->monitor_chr->data.nix.path, - reconnect); - case VIR_DOMAIN_CHR_TYPE_PTY: - return qemudOpenMonitorPty(conn, vm, - vm->monitor_chr->data.file.path, - reconnect); - default: - qemudReportError(conn, NULL, NULL, VIR_ERR_INTERNAL_ERROR, - _("unable to handle monitor type: %s"), - virDomainChrTypeToString(vm->monitor_chr->type)); - return -1; - } -} - -/* Returns -1 for error, 0 success, 1 continue reading */ +/* + * Look at a chunk of data from the QEMU stdout logs and try to + * find a TTY device, as indicated by a line like + * + * char device redirected to /dev/pts/3 + * + * Returns -1 for error, 0 success, 1 continue reading + */ static int -qemudExtractMonitorPath(virConnectPtr conn, - const char *haystack, - size_t *offset, - char **path) +qemudExtractTTYPath(virConnectPtr conn, + const char *haystack, + size_t *offset, + char **path) { static const char needle[] = "char device redirected to"; char *tmp, *dev; @@ -1123,8 +897,8 @@ qemudFindCharDevicePTYs(virConnectPtr conn, for (i = 0 ; i < vm->def->nserials ; i++) { virDomainChrDefPtr chr = vm->def->serials[i]; if (chr->type == VIR_DOMAIN_CHR_TYPE_PTY) { - if ((ret = qemudExtractMonitorPath(conn, output, &offset, - &chr->data.file.path)) != 0) + if ((ret = qemudExtractTTYPath(conn, output, &offset, + &chr->data.file.path)) != 0) return ret; } } @@ -1133,8 +907,8 @@ qemudFindCharDevicePTYs(virConnectPtr conn, for (i = 0 ; i < vm->def->nparallels ; i++) { virDomainChrDefPtr chr = vm->def->parallels[i]; if (chr->type == VIR_DOMAIN_CHR_TYPE_PTY) { - if ((ret = qemudExtractMonitorPath(conn, output, &offset, - &chr->data.file.path)) != 0) + if ((ret = qemudExtractTTYPath(conn, output, &offset, + &chr->data.file.path)) != 0) return ret; } } @@ -1171,7 +945,7 @@ qemudWaitForMonitor(virConnectPtr conn, return -1; } - if (qemudOpenMonitor(conn, vm, 0) < 0) + if (qemuConnectMonitor(vm, 0) < 0) return -1; return 0; diff --git a/src/qemu/qemu_monitor.c b/src/qemu/qemu_monitor.c new file mode 100644 index 0000000..ab1a98c --- /dev/null +++ b/src/qemu/qemu_monitor.c @@ -0,0 +1,268 @@ +/* + * qemu_monitor.c: interaction with QEMU monitor console + * + * Copyright (C) 2006-2009 Red Hat, Inc. + * Copyright (C) 2006 Daniel P. Berrange + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Author: Daniel P. Berrange <berrange@redhat.com> + */ + +#include <config.h> + +#include <poll.h> +#include <sys/un.h> +#include <unistd.h> +#include <fcntl.h> + +#include "qemu_monitor.h" +#include "qemu_conf.h" +#include "event.h" +#include "virterror_internal.h" + +#define VIR_FROM_THIS VIR_FROM_QEMU + +/* Return -1 for error, 1 to continue reading and 0 for success */ +typedef int qemuMonitorHandleOutput(virDomainObjPtr vm, + const char *output, + int fd); + +/* + * Returns -1 for error, 0 on end-of-file, 1 for success + */ +static int +qemuMonitorReadOutput(virDomainObjPtr vm, + int fd, + char *buf, + size_t buflen, + qemuMonitorHandleOutput func, + const char *what, + int timeout) +{ + size_t got = 0; + buf[0] = '\0'; + timeout *= 1000; /* poll wants milli seconds */ + + /* Consume & discard the initial greeting */ + while (got < (buflen-1)) { + ssize_t ret; + + ret = read(fd, buf+got, buflen-got-1); + + if (ret < 0) { + struct pollfd pfd = { .fd = fd, .events = POLLIN }; + if (errno == EINTR) + continue; + + if (errno != EAGAIN) { + virReportSystemError(NULL, errno, + _("Failure while reading %s startup output"), + what); + return -1; + } + + ret = poll(&pfd, 1, timeout); + if (ret == 0) { + qemudReportError(NULL, NULL, NULL, VIR_ERR_INTERNAL_ERROR, + _("Timed out while reading %s startup output"), what); + return -1; + } else if (ret == -1) { + if (errno != EINTR) { + virReportSystemError(NULL, errno, + _("Failure while reading %s startup output"), + what); + return -1; + } + } else { + /* Make sure we continue loop & read any further data + available before dealing with EOF */ + if (pfd.revents & (POLLIN | POLLHUP)) + continue; + + qemudReportError(NULL, NULL, NULL, VIR_ERR_INTERNAL_ERROR, + _("Failure while reading %s startup output"), what); + return -1; + } + } else if (ret == 0) { + return 0; + } else { + got += ret; + buf[got] = '\0'; + ret = func(vm, buf, fd); + if (ret == -1) + return -1; + if (ret == 1) + continue; + return 1; + } + } + + qemudReportError(NULL, NULL, NULL, VIR_ERR_INTERNAL_ERROR, + _("Out of space while reading %s startup output"), what); + return -1; + +} + +static int +qemuMonitorCheckPrompt(virDomainObjPtr vm, + const char *output, + int fd) +{ + if (strstr(output, "(qemu) ") == NULL) + return 1; /* keep reading */ + + vm->monitor = fd; + + return 0; +} + +static int +qemuMonitorOpenCommon(virDomainObjPtr vm, + int monfd, + int reconnect) +{ + char buf[1024]; + int ret; + + if (virSetCloseExec(monfd) < 0) { + qemudReportError(NULL, NULL, NULL, VIR_ERR_INTERNAL_ERROR, + "%s", _("Unable to set monitor close-on-exec flag")); + return -1; + } + if (virSetNonBlock(monfd) < 0) { + qemudReportError(NULL, NULL, NULL, VIR_ERR_INTERNAL_ERROR, + "%s", _("Unable to put monitor into non-blocking mode")); + return -1; + } + + if (!reconnect) { + if (qemuMonitorReadOutput(vm, monfd, + buf, sizeof(buf), + qemuMonitorCheckPrompt, + "monitor", 10) <= 0) + ret = -1; + else + ret = 0; + } else { + vm->monitor = monfd; + ret = 0; + } + + if (ret != 0) + return ret; + + return 0; +} + +static int +qemuMonitorOpenUnix(virDomainObjPtr vm, + const char *monitor, + int reconnect) +{ + struct sockaddr_un addr; + int monfd; + int timeout = 3; /* In seconds */ + int ret, i = 0; + + if ((monfd = socket(AF_UNIX, SOCK_STREAM, 0)) < 0) { + virReportSystemError(NULL, errno, + "%s", _("failed to create socket")); + return -1; + } + + memset(&addr, 0, sizeof(addr)); + addr.sun_family = AF_UNIX; + if (virStrcpyStatic(addr.sun_path, monitor) == NULL) { + qemudReportError(NULL, NULL, NULL, VIR_ERR_INTERNAL_ERROR, + _("Monitor path %s too big for destination"), monitor); + goto error; + } + + do { + ret = connect(monfd, (struct sockaddr *) &addr, sizeof(addr)); + + if (ret == 0) + break; + + if (errno == ENOENT || errno == ECONNREFUSED) { + /* ENOENT : Socket may not have shown up yet + * ECONNREFUSED : Leftover socket hasn't been removed yet */ + continue; + } + + virReportSystemError(NULL, errno, "%s", + _("failed to connect to monitor socket")); + goto error; + + } while ((++i <= timeout*5) && (usleep(.2 * 1000000) <= 0)); + + if (ret != 0) { + virReportSystemError(NULL, errno, "%s", + _("monitor socket did not show up.")); + goto error; + } + + if (qemuMonitorOpenCommon(vm, monfd, reconnect) < 0) + goto error; + + return 0; + +error: + close(monfd); + return -1; +} + +static int +qemuMonitorOpenPty(virDomainObjPtr vm, + const char *monitor, + int reconnect) +{ + int monfd; + + if ((monfd = open(monitor, O_RDWR)) < 0) { + qemudReportError(NULL, NULL, NULL, VIR_ERR_INTERNAL_ERROR, + _("Unable to open monitor path %s"), monitor); + return -1; + } + + if (qemuMonitorOpenCommon(vm, monfd, reconnect) < 0) + goto error; + + return 0; + +error: + close(monfd); + return -1; +} + +int +qemuMonitorOpen(virDomainObjPtr vm, + int reconnect) +{ + switch (vm->monitor_chr->type) { + case VIR_DOMAIN_CHR_TYPE_UNIX: + return qemuMonitorOpenUnix(vm, vm->monitor_chr->data.nix.path, + reconnect); + case VIR_DOMAIN_CHR_TYPE_PTY: + return qemuMonitorOpenPty(vm, vm->monitor_chr->data.file.path, + reconnect); + default: + qemudReportError(NULL, NULL, NULL, VIR_ERR_INTERNAL_ERROR, + _("unable to handle monitor type: %s"), + virDomainChrTypeToString(vm->monitor_chr->type)); + return -1; + } +} diff --git a/src/qemu/qemu_monitor.h b/src/qemu/qemu_monitor.h new file mode 100644 index 0000000..bdeafe0 --- /dev/null +++ b/src/qemu/qemu_monitor.h @@ -0,0 +1,37 @@ +/* + * qemu_monitor.h: interaction with QEMU monitor console + * + * Copyright (C) 2006-2009 Red Hat, Inc. + * Copyright (C) 2006 Daniel P. Berrange + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Author: Daniel P. Berrange <berrange@redhat.com> + */ + + +#ifndef QEMU_MONITOR_H +#define QEMU_MONITOR_H + +#include "internal.h" + +#include "domain_conf.h" + +int qemuMonitorOpen(virDomainObjPtr vm, + int reconnect); + + + +#endif /* QEMU_MONITOR_H */ -- 1.6.2.5

On Tue, Nov 03, 2009 at 02:49:58PM -0500, Daniel P. Berrange wrote:
The qemu_driver.c code should not contain any code that interacts with the QEMU monitor at a low level. A previous commit moved all the command invocations out. This change moves out the code which actually opens the monitor device. [...] +static int +qemuConnectMonitor(virDomainObjPtr vm, int reconnect) +{ + int rc; + if ((rc = qemuMonitorOpen(vm, reconnect)) != 0) { + VIR_ERROR(_("Failed to connect monitor for %s: %d\n"), + vm->def->name, rc); + return -1; + }
-static int qemudOpenMonitor(virConnectPtr conn, - virDomainObjPtr vm, - int reconnect); + if ((vm->monitorWatch = virEventAddHandle(vm->monitor, + VIR_EVENT_HANDLE_HANGUP | VIR_EVENT_HANDLE_ERROR, + qemudDispatchVMEvent,
Let's try to keep at 80 columns, even if we don't reindent exactly under the opening brace [...]
+static int +qemuMonitorReadOutput(virDomainObjPtr vm, + int fd, + char *buf, + size_t buflen, + qemuMonitorHandleOutput func, + const char *what, + int timeout) +{ [...] + if (errno != EAGAIN) { + virReportSystemError(NULL, errno, + _("Failure while reading %s startup output"), + what); + return -1; + } + + ret = poll(&pfd, 1, timeout); + if (ret == 0) { + qemudReportError(NULL, NULL, NULL, VIR_ERR_INTERNAL_ERROR, + _("Timed out while reading %s startup output"), what); + return -1; + } else if (ret == -1) { + if (errno != EINTR) { + virReportSystemError(NULL, errno, + _("Failure while reading %s startup output"), + what); + return -1; + } + } else { + /* Make sure we continue loop & read any further data + available before dealing with EOF */ + if (pfd.revents & (POLLIN | POLLHUP)) + continue; + + qemudReportError(NULL, NULL, NULL, VIR_ERR_INTERNAL_ERROR, + _("Failure while reading %s startup output"), what); + return -1;
same even if we might want to preserve the commit as a pure cut and paste, then let's resize to keep in 80 column as a second commit. [...]
+static int +qemuMonitorCheckPrompt(virDomainObjPtr vm, + const char *output, + int fd) +{ + if (strstr(output, "(qemu) ") == NULL) + return 1; /* keep reading */
just wondering, wouldn't it be safer to try to find the previous end of line too ? ACK ! Daniel -- Daniel Veillard | libxml Gnome XML XSLT toolkit http://xmlsoft.org/ daniel@veillard.com | Rpmfind RPM search engine http://rpmfind.net/ http://veillard.com/ | virtualization library http://libvirt.org/

On Wed, Nov 04, 2009 at 12:09:39PM +0100, Daniel Veillard wrote:
On Tue, Nov 03, 2009 at 02:49:58PM -0500, Daniel P. Berrange wrote:
The qemu_driver.c code should not contain any code that interacts with the QEMU monitor at a low level. A previous commit moved all the command invocations out. This change moves out the code which actually opens the monitor device.
[...]
+static int +qemuMonitorCheckPrompt(virDomainObjPtr vm, + const char *output, + int fd) +{ + if (strstr(output, "(qemu) ") == NULL) + return 1; /* keep reading */
just wondering, wouldn't it be safer to try to find the previous end of line too ?
This particular piece of code is deleted in a later patch in the series Daniel -- |: Red Hat, Engineering, London -o- http://people.redhat.com/berrange/ :| |: http://libvirt.org -o- http://virt-manager.org -o- http://ovirt.org :| |: http://autobuild.org -o- http://search.cpan.org/~danberr/ :| |: GnuPG: 7D3B9505 -o- F3C9 553F A1DA 4AC2 5648 23C1 B3DF F742 7D3B 9505 :|

Introduce a new qemuDomainObjPrivate object which is used to store the private QEMU specific data associated with each virDomainObjPtr instance. This contains a single member, an instance of the new qemuMonitorPtr object which encapsulates the QEMU monitor state. The internals of the latter are private to the qemu_monitor* files, not to be shown to qemu_driver.c * src/qemu/qemu_conf.h: Definition of qemuDomainObjPrivate. * src/qemu/qemu_driver.c: Register a functions for creating and freeing qemuDomainObjPrivate instances with the domain capabilities. Remove the qemudDispatchVMEvent() watch since I/O watches are now handled by the monitor code itself. Pass a new qemuHandleMonitorEOF() callback into qemuMonitorOpen to allow notification when the monitor quits. * src/qemu/qemu_monitor.c, src/qemu/qemu_monitor.h: Introduce the 'qemuMonitor' object. Temporarily add new APIs qemuMonitorWrite, qemuMonitorRead, qemuMonitorWaitForInput to allow text based monitor impl to perform I/O. * src/qemu/qemu_monitor_text.c: Call APIs for reading/writing to monitor instead of accessing the file handle directly. --- src/qemu/qemu_conf.h | 11 +++ src/qemu/qemu_driver.c | 153 +++++++++++++++++----------------- src/qemu/qemu_monitor.c | 186 +++++++++++++++++++++++++++++++++++++----- src/qemu/qemu_monitor.h | 28 ++++++- src/qemu/qemu_monitor_text.c | 75 +++-------------- 5 files changed, 292 insertions(+), 161 deletions(-) diff --git a/src/qemu/qemu_conf.h b/src/qemu/qemu_conf.h index f9a970f..171bf90 100644 --- a/src/qemu/qemu_conf.h +++ b/src/qemu/qemu_conf.h @@ -36,6 +36,7 @@ #include "security/security_driver.h" #include "cgroup.h" #include "pci.h" +#include "qemu_monitor.h" #define qemudDebug(fmt, ...) do {} while(0) @@ -128,6 +129,16 @@ struct qemud_driver { pciDeviceList *activePciHostdevs; }; +/* XXX temporarily exposed. + * This will be moved back into qemu_driver.c, once the + * qemu_monitor* code is refactored a little more + */ +typedef struct _qemuDomainObjPrivate qemuDomainObjPrivate; +typedef qemuDomainObjPrivate *qemuDomainObjPrivatePtr; +struct _qemuDomainObjPrivate { + qemuMonitorPtr mon; +}; + /* Port numbers used for KVM migration. */ #define QEMUD_MIGRATION_FIRST_PORT 49152 diff --git a/src/qemu/qemu_driver.c b/src/qemu/qemu_driver.c index 8535fac..e985543 100644 --- a/src/qemu/qemu_driver.c +++ b/src/qemu/qemu_driver.c @@ -92,11 +92,6 @@ static void qemuDomainEventFlush(int timer, void *opaque); static void qemuDomainEventQueue(struct qemud_driver *driver, virDomainEventPtr event); -static void qemudDispatchVMEvent(int watch, - int fd, - int events, - void *opaque); - static int qemudStartVMDaemon(virConnectPtr conn, struct qemud_driver *driver, virDomainObjPtr vm, @@ -117,6 +112,30 @@ static int qemuUpdateActivePciHostdevs(struct qemud_driver *driver, static struct qemud_driver *qemu_driver = NULL; + +static void *qemuDomainObjPrivateAlloc(void) +{ + qemuDomainObjPrivatePtr priv; + + if (VIR_ALLOC(priv) < 0) + return NULL; + + return priv; +} + +static void qemuDomainObjPrivateFree(void *data) +{ + qemuDomainObjPrivatePtr priv = data; + + /* This should never be non-NULL if we get here, but just in case... */ + if (priv->mon) { + VIR_ERROR0("Unexpected QEMU monitor still active during domain deletion"); + qemuMonitorClose(priv->mon); + } + VIR_FREE(priv); +} + + static int qemuCgroupControllerActive(struct qemud_driver *driver, int controller) { @@ -291,21 +310,53 @@ qemudRemoveDomainStatus(virConnectPtr conn, return 0; } + +/* + * This is a callback registered with a qemuMonitorPtr instance, + * and to be invoked when the monitor console hits an end of file + * condition, or error, thus indicating VM shutdown should be + * performed + */ +static void +qemuHandleMonitorEOF(qemuMonitorPtr mon ATTRIBUTE_UNUSED, + virDomainObjPtr vm, + int hasError) { + struct qemud_driver *driver = qemu_driver; + virDomainEventPtr event = NULL; + + qemuDriverLock(driver); + virDomainObjLock(vm); + qemuDriverUnlock(driver); + + event = virDomainEventNewFromObj(vm, + VIR_DOMAIN_EVENT_STOPPED, + hasError ? + VIR_DOMAIN_EVENT_STOPPED_FAILED : + VIR_DOMAIN_EVENT_STOPPED_SHUTDOWN); + + qemudShutdownVMDaemon(NULL, driver, vm); + if (!vm->persistent) + virDomainRemoveInactive(&driver->domains, vm); + else + virDomainObjUnlock(vm); + + if (event) { + qemuDriverLock(driver); + qemuDomainEventQueue(driver, event); + qemuDriverUnlock(driver); + } +} + + static int qemuConnectMonitor(virDomainObjPtr vm, int reconnect) { - int rc; - if ((rc = qemuMonitorOpen(vm, reconnect)) != 0) { - VIR_ERROR(_("Failed to connect monitor for %s: %d\n"), - vm->def->name, rc); - return -1; - } + qemuDomainObjPrivatePtr priv = vm->privateData; - if ((vm->monitorWatch = virEventAddHandle(vm->monitor, - VIR_EVENT_HANDLE_HANGUP | VIR_EVENT_HANDLE_ERROR, - qemudDispatchVMEvent, - vm, NULL)) < 0) + if ((priv->mon = qemuMonitorOpen(vm, reconnect, qemuHandleMonitorEOF)) == NULL) { + VIR_ERROR(_("Failed to connect monitor for %s\n"), vm->def->name); return -1; + } return 0; } @@ -350,7 +401,7 @@ error: } /** - * qemudReconnectVMs + * qemudReconnectDomains * * Try to re-open the resources for live VMs that we care * about. @@ -552,6 +603,9 @@ qemudStartup(int privileged) { if ((qemu_driver->caps = qemudCapsInit(NULL)) == NULL) goto out_of_memory; + qemu_driver->caps->privateDataAllocFunc = qemuDomainObjPrivateAlloc; + qemu_driver->caps->privateDataFreeFunc = qemuDomainObjPrivateFree; + if ((qemu_driver->activePciHostdevs = pciDeviceListNew(NULL)) == NULL) goto error; @@ -1952,6 +2006,7 @@ static void qemudShutdownVMDaemon(virConnectPtr conn, virDomainObjPtr vm) { int ret; int retries = 0; + qemuDomainObjPrivatePtr priv = vm->privateData; if (!virDomainObjIsActive(vm)) return; @@ -1964,15 +2019,11 @@ static void qemudShutdownVMDaemon(virConnectPtr conn, _("Failed to send SIGTERM to %s (%d)"), vm->def->name, vm->pid); - if (vm->monitorWatch != -1) { - virEventRemoveHandle(vm->monitorWatch); - vm->monitorWatch = -1; + if (priv->mon) { + qemuMonitorClose(priv->mon); + priv->mon = NULL; } - if (vm->monitor != -1) - close(vm->monitor); - vm->monitor = -1; - if (vm->monitor_chr) { if (vm->monitor_chr->type == VIR_DOMAIN_CHR_TYPE_UNIX) unlink(vm->monitor_chr->data.nix.path); @@ -2027,59 +2078,6 @@ retry: } -static void -qemudDispatchVMEvent(int watch, int fd, int events, void *opaque) { - struct qemud_driver *driver = qemu_driver; - virDomainObjPtr vm = opaque; - virDomainEventPtr event = NULL; - int quit = 0, failed = 0; - - /* XXX Normally we have to lock the driver first, to protect - * against someone adding/removing the domain. We know, - * however, then if we're getting data in this callback - * the VM must be running. Nowhere is allowed to remove - * a domain while it is running, so it is safe to not - * lock the driver here... */ - qemuDriverLock(driver); - virDomainObjLock(vm); - qemuDriverUnlock(driver); - - if (vm->monitor != fd || vm->monitorWatch != watch) { - failed = 1; - } else { - if (events & (VIR_EVENT_HANDLE_HANGUP | VIR_EVENT_HANDLE_ERROR)) - quit = 1; - else { - VIR_ERROR(_("unhandled fd event %d for %s"), - events, vm->def->name); - failed = 1; - } - } - - if (failed || quit) { - event = virDomainEventNewFromObj(vm, - VIR_DOMAIN_EVENT_STOPPED, - quit ? - VIR_DOMAIN_EVENT_STOPPED_SHUTDOWN : - VIR_DOMAIN_EVENT_STOPPED_FAILED); - qemudShutdownVMDaemon(NULL, driver, vm); - if (!vm->persistent) { - virDomainRemoveInactive(&driver->domains, - vm); - vm = NULL; - } - } - - virDomainObjUnlock(vm); - if (event) { - qemuDriverLock(driver); - qemuDomainEventQueue(driver, event); - qemuDriverUnlock(driver); - } -} - - - static virDrvOpenStatus qemudOpen(virConnectPtr conn, virConnectAuthPtr auth ATTRIBUTE_UNUSED, int flags ATTRIBUTE_UNUSED) { @@ -2213,6 +2211,9 @@ static char *qemudGetCapabilities(virConnectPtr conn) { goto cleanup; } + caps->privateDataAllocFunc = qemuDomainObjPrivateAlloc; + caps->privateDataFreeFunc = qemuDomainObjPrivateFree; + if (qemu_driver->securityDriver && qemudSecurityCapsInit(qemu_driver->securityDriver, caps) < 0) { virCapabilitiesFree(caps); diff --git a/src/qemu/qemu_monitor.c b/src/qemu/qemu_monitor.c index ab1a98c..5f7e20c 100644 --- a/src/qemu/qemu_monitor.c +++ b/src/qemu/qemu_monitor.c @@ -32,13 +32,24 @@ #include "qemu_conf.h" #include "event.h" #include "virterror_internal.h" +#include "memory.h" +#include "logging.h" #define VIR_FROM_THIS VIR_FROM_QEMU +struct _qemuMonitor { + int fd; + int watch; + int hasSendFD; + + virDomainObjPtr vm; + + qemuMonitorEOFNotify eofCB; +}; + /* Return -1 for error, 1 to continue reading and 0 for success */ typedef int qemuMonitorHandleOutput(virDomainObjPtr vm, - const char *output, - int fd); + const char *output); /* * Returns -1 for error, 0 on end-of-file, 1 for success @@ -101,7 +112,7 @@ qemuMonitorReadOutput(virDomainObjPtr vm, } else { got += ret; buf[got] = '\0'; - ret = func(vm, buf, fd); + ret = func(vm, buf); if (ret == -1) return -1; if (ret == 1) @@ -117,15 +128,12 @@ qemuMonitorReadOutput(virDomainObjPtr vm, } static int -qemuMonitorCheckPrompt(virDomainObjPtr vm, - const char *output, - int fd) +qemuMonitorCheckPrompt(virDomainObjPtr vm ATTRIBUTE_UNUSED, + const char *output) { if (strstr(output, "(qemu) ") == NULL) return 1; /* keep reading */ - vm->monitor = fd; - return 0; } @@ -157,14 +165,10 @@ qemuMonitorOpenCommon(virDomainObjPtr vm, else ret = 0; } else { - vm->monitor = monfd; ret = 0; } - if (ret != 0) - return ret; - - return 0; + return ret; } static int @@ -218,7 +222,7 @@ qemuMonitorOpenUnix(virDomainObjPtr vm, if (qemuMonitorOpenCommon(vm, monfd, reconnect) < 0) goto error; - return 0; + return monfd; error: close(monfd); @@ -241,28 +245,168 @@ qemuMonitorOpenPty(virDomainObjPtr vm, if (qemuMonitorOpenCommon(vm, monfd, reconnect) < 0) goto error; - return 0; + return monfd; error: close(monfd); return -1; } -int + +static void +qemuMonitorIO(int watch, int fd, int events, void *opaque) { + qemuMonitorPtr mon = opaque; + int quit = 0, failed = 0; + + if (mon->fd != fd || mon->watch != watch) { + VIR_ERROR0(_("event from unexpected fd/watch")); + failed = 1; + } else { + if (events & (VIR_EVENT_HANDLE_HANGUP | VIR_EVENT_HANDLE_ERROR)) + quit = 1; + else { + VIR_ERROR(_("unhandled fd event %d for monitor fd %d"), + events, mon->fd); + failed = 1; + } + } + + mon->eofCB(mon, mon->vm, failed); +} + + + + + +qemuMonitorPtr qemuMonitorOpen(virDomainObjPtr vm, - int reconnect) + int reconnect, + qemuMonitorEOFNotify eofCB) { + qemuMonitorPtr mon; + + if (VIR_ALLOC(mon) < 0) { + virReportOOMError(NULL); + return NULL; + } + + mon->fd = -1; + mon->vm = vm; + mon->eofCB = eofCB; + switch (vm->monitor_chr->type) { case VIR_DOMAIN_CHR_TYPE_UNIX: - return qemuMonitorOpenUnix(vm, vm->monitor_chr->data.nix.path, - reconnect); + mon->hasSendFD = 1; + mon->fd = qemuMonitorOpenUnix(vm, vm->monitor_chr->data.nix.path, + reconnect); + break; + case VIR_DOMAIN_CHR_TYPE_PTY: - return qemuMonitorOpenPty(vm, vm->monitor_chr->data.file.path, - reconnect); + mon->fd = qemuMonitorOpenPty(vm, vm->monitor_chr->data.file.path, + reconnect); + break; + default: qemudReportError(NULL, NULL, NULL, VIR_ERR_INTERNAL_ERROR, _("unable to handle monitor type: %s"), virDomainChrTypeToString(vm->monitor_chr->type)); + goto cleanup; + } + + if ((mon->watch = virEventAddHandle(mon->fd, + VIR_EVENT_HANDLE_HANGUP | VIR_EVENT_HANDLE_ERROR, + qemuMonitorIO, + mon, NULL)) < 0) { + qemudReportError(NULL, NULL, NULL, VIR_ERR_INTERNAL_ERROR, "%s", + _("unable to register monitor events")); + goto cleanup; + } + + return mon; + +cleanup: + qemuMonitorClose(mon); + return NULL; +} + + +void qemuMonitorClose(qemuMonitorPtr mon) +{ + if (!mon) + return; + + if (mon->watch) + virEventRemoveHandle(mon->watch); + + if (mon->fd != -1) + close(mon->fd); + VIR_FREE(mon); +} + + +int qemuMonitorWrite(qemuMonitorPtr mon, + const char *data, + size_t len) +{ + return safewrite(mon->fd, data, len); +} + +int qemuMonitorWriteWithFD(qemuMonitorPtr mon, + const char *data, + size_t len, + int fd) +{ + struct msghdr msg; + struct iovec iov[1]; + ssize_t ret; + char control[CMSG_SPACE(sizeof(int))]; + struct cmsghdr *cmsg; + + if (!mon->hasSendFD) { + errno = EINVAL; return -1; } + + memset(&msg, 0, sizeof(msg)); + + iov[0].iov_base = (void *)data; + iov[0].iov_len = len; + + msg.msg_iov = iov; + msg.msg_iovlen = 1; + + msg.msg_control = control; + msg.msg_controllen = sizeof(control); + + cmsg = CMSG_FIRSTHDR(&msg); + cmsg->cmsg_len = CMSG_LEN(sizeof(int)); + cmsg->cmsg_level = SOL_SOCKET; + cmsg->cmsg_type = SCM_RIGHTS; + memcpy(CMSG_DATA(cmsg), &fd, sizeof(int)); + + do { + ret = sendmsg(mon->fd, &msg, 0); + } while (ret < 0 && errno == EINTR); + + return ret == len ? 0 : -1; +} + +int qemuMonitorRead(qemuMonitorPtr mon, + char *data, + size_t len) +{ + return read(mon->fd, data, len); +} + +int qemuMonitorWaitForInput(qemuMonitorPtr mon) +{ + struct pollfd fd = { mon->fd, POLLIN | POLLERR | POLLHUP, 0 }; + +retry: + if (poll(&fd, 1, -1) < 0) { + if (errno == EINTR) + goto retry; + return -1; + } + return 0; } diff --git a/src/qemu/qemu_monitor.h b/src/qemu/qemu_monitor.h index bdeafe0..e863e20 100644 --- a/src/qemu/qemu_monitor.h +++ b/src/qemu/qemu_monitor.h @@ -29,9 +29,33 @@ #include "domain_conf.h" -int qemuMonitorOpen(virDomainObjPtr vm, - int reconnect); +typedef struct _qemuMonitor qemuMonitor; +typedef qemuMonitor *qemuMonitorPtr; +typedef void (*qemuMonitorEOFNotify)(qemuMonitorPtr mon, + virDomainObjPtr vm, + int withError); + +qemuMonitorPtr qemuMonitorOpen(virDomainObjPtr vm, + int reconnect, + qemuMonitorEOFNotify eofCB); + +void qemuMonitorClose(qemuMonitorPtr mon); + +int qemuMonitorWrite(qemuMonitorPtr mon, + const char *data, + size_t len); + +int qemuMonitorWriteWithFD(qemuMonitorPtr mon, + const char *data, + size_t len, + int fd); + +int qemuMonitorRead(qemuMonitorPtr mon, + char *data, + size_t len); + +int qemuMonitorWaitForInput(qemuMonitorPtr mon); #endif /* QEMU_MONITOR_H */ diff --git a/src/qemu/qemu_monitor_text.c b/src/qemu/qemu_monitor_text.c index 66526dc..fa17971 100644 --- a/src/qemu/qemu_monitor_text.c +++ b/src/qemu/qemu_monitor_text.c @@ -142,6 +142,7 @@ static char *qemuMonitorEscapeShell(const char *in) */ static void qemuMonitorDiscardPendingData(virDomainObjPtr vm) { + qemuDomainObjPrivatePtr priv = vm->privateData; char buf[1024]; int ret = 0; @@ -149,54 +150,17 @@ qemuMonitorDiscardPendingData(virDomainObjPtr vm) { * get -1 or 0. Don't bother with detecting * errors, since we'll deal with that better later */ do { - ret = read(vm->monitor, buf, sizeof (buf)-1); + ret = qemuMonitorRead(priv->mon, buf, sizeof (buf)-1); } while (ret > 0); } -static int -qemuMonitorSendUnix(const virDomainObjPtr vm, - const char *cmd, - size_t cmdlen, - int scm_fd) -{ - struct msghdr msg; - struct iovec iov[1]; - ssize_t ret; - - memset(&msg, 0, sizeof(msg)); - - iov[0].iov_base = (void *)cmd; - iov[0].iov_len = cmdlen; - - msg.msg_iov = iov; - msg.msg_iovlen = 1; - - if (scm_fd != -1) { - char control[CMSG_SPACE(sizeof(int))]; - struct cmsghdr *cmsg; - - msg.msg_control = control; - msg.msg_controllen = sizeof(control); - - cmsg = CMSG_FIRSTHDR(&msg); - cmsg->cmsg_len = CMSG_LEN(sizeof(int)); - cmsg->cmsg_level = SOL_SOCKET; - cmsg->cmsg_type = SCM_RIGHTS; - memcpy(CMSG_DATA(cmsg), &scm_fd, sizeof(int)); - } - - do { - ret = sendmsg(vm->monitor, &msg, 0); - } while (ret < 0 && errno == EINTR); - - return ret == cmdlen ? 0 : -1; -} static int qemuMonitorSend(const virDomainObjPtr vm, const char *cmd, int scm_fd) { + qemuDomainObjPrivatePtr priv = vm->privateData; char *full; size_t len; int ret = -1; @@ -206,20 +170,11 @@ qemuMonitorSend(const virDomainObjPtr vm, len = strlen(full); - switch (vm->monitor_chr->type) { - case VIR_DOMAIN_CHR_TYPE_UNIX: - if (qemuMonitorSendUnix(vm, full, len, scm_fd) < 0) - goto out; - break; - default: - case VIR_DOMAIN_CHR_TYPE_PTY: - if (safewrite(vm->monitor, full, len) != len) - goto out; - break; - } + if (scm_fd == -1) + ret = qemuMonitorWrite(priv->mon, full, len); + else + ret = qemuMonitorWriteWithFD(priv->mon, full, len, scm_fd); - ret = 0; -out: VIR_FREE(full); return ret; } @@ -232,12 +187,13 @@ qemuMonitorCommandWithHandler(const virDomainObjPtr vm, void *handlerData, int scm_fd, char **reply) { + qemuDomainObjPrivatePtr priv = vm->privateData; int size = 0; char *buf = NULL; /* Should never happen, but just in case, protect * against null monitor (ocurrs when VM is inactive) */ - if (!vm->monitor_chr) + if (!priv->mon) return -1; qemuMonitorDiscardPendingData(vm); @@ -249,13 +205,10 @@ qemuMonitorCommandWithHandler(const virDomainObjPtr vm, *reply = NULL; for (;;) { - struct pollfd fd = { vm->monitor, POLLIN | POLLERR | POLLHUP, 0 }; - char *tmp; - /* Read all the data QEMU has sent thus far */ for (;;) { char data[1024]; - int got = read(vm->monitor, data, sizeof(data)); + int got = qemuMonitorRead(priv->mon, data, sizeof(data)); if (got == 0) goto error; @@ -277,6 +230,7 @@ qemuMonitorCommandWithHandler(const virDomainObjPtr vm, /* Look for QEMU prompt to indicate completion */ if (buf) { char *foundPrompt; + char *tmp; if (extraPrompt && (foundPrompt = strstr(buf, extraPrompt)) != NULL) { @@ -312,13 +266,10 @@ qemuMonitorCommandWithHandler(const virDomainObjPtr vm, break; } } - pollagain: + /* Need to wait for more data */ - if (poll(&fd, 1, -1) < 0) { - if (errno == EINTR) - goto pollagain; + if (qemuMonitorWaitForInput(priv->mon) < 0) goto error; - } } *reply = buf; DEBUG("reply='%s'", buf); -- 1.6.2.5

On Tue, Nov 03, 2009 at 02:49:59PM -0500, Daniel P. Berrange wrote:
Introduce a new qemuDomainObjPrivate object which is used to store the private QEMU specific data associated with each virDomainObjPtr instance. This contains a single member, an instance of the new qemuMonitorPtr object which encapsulates the QEMU monitor state. The internals of the latter are private to the qemu_monitor* files, not to be shown to qemu_driver.c
* src/qemu/qemu_conf.h: Definition of qemuDomainObjPrivate. * src/qemu/qemu_driver.c: Register a functions for creating and freeing qemuDomainObjPrivate instances with the domain capabilities. Remove the qemudDispatchVMEvent() watch since I/O watches are now handled by the monitor code itself. Pass a new qemuHandleMonitorEOF() callback into qemuMonitorOpen to allow notification when the monitor quits. * src/qemu/qemu_monitor.c, src/qemu/qemu_monitor.h: Introduce the 'qemuMonitor' object. Temporarily add new APIs qemuMonitorWrite, qemuMonitorRead, qemuMonitorWaitForInput to allow text based monitor impl to perform I/O. * src/qemu/qemu_monitor_text.c: Call APIs for reading/writing to monitor instead of accessing the file handle directly. [...] +int qemuMonitorWriteWithFD(qemuMonitorPtr mon, + const char *data, + size_t len, + int fd) +{ + struct msghdr msg; + struct iovec iov[1]; + ssize_t ret; + char control[CMSG_SPACE(sizeof(int))]; + struct cmsghdr *cmsg; + + if (!mon->hasSendFD) { + errno = EINVAL; return -1; } + + memset(&msg, 0, sizeof(msg)); + + iov[0].iov_base = (void *)data; + iov[0].iov_len = len; + + msg.msg_iov = iov; + msg.msg_iovlen = 1; + + msg.msg_control = control; + msg.msg_controllen = sizeof(control); + + cmsg = CMSG_FIRSTHDR(&msg); + cmsg->cmsg_len = CMSG_LEN(sizeof(int)); + cmsg->cmsg_level = SOL_SOCKET; + cmsg->cmsg_type = SCM_RIGHTS; + memcpy(CMSG_DATA(cmsg), &fd, sizeof(int)); + + do { + ret = sendmsg(mon->fd, &msg, 0); + } while (ret < 0 && errno == EINTR); + + return ret == len ? 0 : -1; +}
I must admit I'm a bit puzzled by this function, we are sending both data and a file descriptor ? But it's an in-process exchange, why would that be needed, I'm at loss ... [...]
-static int -qemuMonitorSendUnix(const virDomainObjPtr vm, - const char *cmd, - size_t cmdlen, - int scm_fd) -{ - struct msghdr msg; - struct iovec iov[1]; - ssize_t ret; - - memset(&msg, 0, sizeof(msg)); - - iov[0].iov_base = (void *)cmd; - iov[0].iov_len = cmdlen; - - msg.msg_iov = iov; - msg.msg_iovlen = 1; - - if (scm_fd != -1) { - char control[CMSG_SPACE(sizeof(int))]; - struct cmsghdr *cmsg; - - msg.msg_control = control; - msg.msg_controllen = sizeof(control); - - cmsg = CMSG_FIRSTHDR(&msg); - cmsg->cmsg_len = CMSG_LEN(sizeof(int)); - cmsg->cmsg_level = SOL_SOCKET; - cmsg->cmsg_type = SCM_RIGHTS; - memcpy(CMSG_DATA(cmsg), &scm_fd, sizeof(int)); - } - - do { - ret = sendmsg(vm->monitor, &msg, 0); - } while (ret < 0 && errno == EINTR); - - return ret == cmdlen ? 0 : -1; -}
okay that's the source function, nothing new but still wondering... Other than that I didn't found anything strange, so ACK, but if you have time to explain :-) Daniel -- Daniel Veillard | libxml Gnome XML XSLT toolkit http://xmlsoft.org/ daniel@veillard.com | Rpmfind RPM search engine http://rpmfind.net/ http://veillard.com/ | virtualization library http://libvirt.org/

On Wed, Nov 04, 2009 at 02:37:26PM +0100, Daniel Veillard wrote:
On Tue, Nov 03, 2009 at 02:49:59PM -0500, Daniel P. Berrange wrote:
Introduce a new qemuDomainObjPrivate object which is used to store the private QEMU specific data associated with each virDomainObjPtr instance. This contains a single member, an instance of the new qemuMonitorPtr object which encapsulates the QEMU monitor state. The internals of the latter are private to the qemu_monitor* files, not to be shown to qemu_driver.c
* src/qemu/qemu_conf.h: Definition of qemuDomainObjPrivate. * src/qemu/qemu_driver.c: Register a functions for creating and freeing qemuDomainObjPrivate instances with the domain capabilities. Remove the qemudDispatchVMEvent() watch since I/O watches are now handled by the monitor code itself. Pass a new qemuHandleMonitorEOF() callback into qemuMonitorOpen to allow notification when the monitor quits. * src/qemu/qemu_monitor.c, src/qemu/qemu_monitor.h: Introduce the 'qemuMonitor' object. Temporarily add new APIs qemuMonitorWrite, qemuMonitorRead, qemuMonitorWaitForInput to allow text based monitor impl to perform I/O. * src/qemu/qemu_monitor_text.c: Call APIs for reading/writing to monitor instead of accessing the file handle directly. [...] +int qemuMonitorWriteWithFD(qemuMonitorPtr mon, + const char *data, + size_t len, + int fd) +{ + struct msghdr msg; + struct iovec iov[1]; + ssize_t ret; + char control[CMSG_SPACE(sizeof(int))]; + struct cmsghdr *cmsg; + + if (!mon->hasSendFD) { + errno = EINVAL; return -1; } + + memset(&msg, 0, sizeof(msg)); + + iov[0].iov_base = (void *)data; + iov[0].iov_len = len; + + msg.msg_iov = iov; + msg.msg_iovlen = 1; + + msg.msg_control = control; + msg.msg_controllen = sizeof(control); + + cmsg = CMSG_FIRSTHDR(&msg); + cmsg->cmsg_len = CMSG_LEN(sizeof(int)); + cmsg->cmsg_level = SOL_SOCKET; + cmsg->cmsg_type = SCM_RIGHTS; + memcpy(CMSG_DATA(cmsg), &fd, sizeof(int)); + + do { + ret = sendmsg(mon->fd, &msg, 0); + } while (ret < 0 && errno == EINTR); + + return ret == len ? 0 : -1; +}
I must admit I'm a bit puzzled by this function, we are sending both data and a file descriptor ? But it's an in-process exchange, why would that be needed, I'm at loss ...
The need to send a file descriptor is pretty rare. It is only used when hottplugging network device. libvirt sends the FD of the TAP device over to QEMU. In all other places this capability is unused. Regards, Daniel -- |: Red Hat, Engineering, London -o- http://people.redhat.com/berrange/ :| |: http://libvirt.org -o- http://virt-manager.org -o- http://ovirt.org :| |: http://autobuild.org -o- http://search.cpan.org/~danberr/ :| |: GnuPG: 7D3B9505 -o- F3C9 553F A1DA 4AC2 5648 23C1 B3DF F742 7D3B 9505 :|

Decouple the monitor code from the virDomainDefPtr structure by moving the disk encryption lookup code back into the qemu_driver.c file. Instead provide a function callback to the monitor code which can be invoked to retrieve encryption data as required. * src/qemu/qemu_driver.c: Add findDomainDiskEncryption, and findVolumeQcowPassphrase. Pass address of the method findVolumeQcowPassphrase into qemuMonitorOpen() * src/qemu/qemu_monitor.c: Associate a disk encryption function callback with the qemuMonitorPtr object. * src/qemu/qemu_monitor_text.c: Remove findDomainDiskEncryption and findVolumeQcowPassphrase. --- src/qemu/qemu_driver.c | 110 ++++++++++++++++++++++++++++++++++++++++++ src/qemu/qemu_monitor.c | 22 ++++++++ src/qemu/qemu_monitor.h | 20 ++++++++ src/qemu/qemu_monitor_text.c | 103 ++------------------------------------- 4 files changed, 157 insertions(+), 98 deletions(-) diff --git a/src/qemu/qemu_driver.c b/src/qemu/qemu_driver.c index e985543..30cc66e 100644 --- a/src/qemu/qemu_driver.c +++ b/src/qemu/qemu_driver.c @@ -348,6 +348,113 @@ qemuHandleMonitorEOF(qemuMonitorPtr mon ATTRIBUTE_UNUSED, } +static virStorageEncryptionPtr +findDomainDiskEncryption(virConnectPtr conn, virDomainObjPtr vm, + const char *path) +{ + bool seen_volume; + int i; + + seen_volume = false; + for (i = 0; i < vm->def->ndisks; i++) { + virDomainDiskDefPtr disk; + + disk = vm->def->disks[i]; + if (disk->src != NULL && STREQ(disk->src, path)) { + seen_volume = true; + if (disk->encryption != NULL) + return disk->encryption; + } + } + if (seen_volume) + qemudReportError(conn, NULL, NULL, VIR_ERR_INVALID_DOMAIN, + _("missing <encryption> for volume %s"), path); + else + qemudReportError(conn, NULL, NULL, VIR_ERR_INTERNAL_ERROR, + _("unexpected passphrase request for volume %s"), + path); + return NULL; +} + + +static int +findVolumeQcowPassphrase(qemuMonitorPtr mon ATTRIBUTE_UNUSED, + virConnectPtr conn, + virDomainObjPtr vm, + const char *path, + char **secretRet, + size_t *secretLen) +{ + virStorageEncryptionPtr enc; + virSecretPtr secret; + char *passphrase; + unsigned char *data; + size_t size; + + if (!conn) { + qemudReportError(NULL, NULL, NULL, VIR_ERR_NO_SUPPORT, + "%s", _("cannot find secrets without a connection")); + return -1; + } + + if (conn->secretDriver == NULL || + conn->secretDriver->lookupByUUID == NULL || + conn->secretDriver->getValue == NULL) { + qemudReportError(conn, NULL, NULL, VIR_ERR_NO_SUPPORT, "%s", + _("secret storage not supported")); + return -1; + } + + enc = findDomainDiskEncryption(conn, vm, path); + if (enc == NULL) + return -1; + + if (enc->format != VIR_STORAGE_ENCRYPTION_FORMAT_QCOW || + enc->nsecrets != 1 || + enc->secrets[0]->type != + VIR_STORAGE_ENCRYPTION_SECRET_TYPE_PASSPHRASE) { + qemudReportError(conn, NULL, NULL, VIR_ERR_INVALID_DOMAIN, + _("invalid <encryption> for volume %s"), path); + return -1; + } + + secret = conn->secretDriver->lookupByUUID(conn, + enc->secrets[0]->uuid); + if (secret == NULL) + return -1; + data = conn->secretDriver->getValue(secret, &size, + VIR_SECRET_GET_VALUE_INTERNAL_CALL); + virUnrefSecret(secret); + if (data == NULL) + return -1; + + if (memchr(data, '\0', size) != NULL) { + memset(data, 0, size); + VIR_FREE(data); + qemudReportError(conn, NULL, NULL, VIR_ERR_INVALID_SECRET, + _("format='qcow' passphrase for %s must not contain a " + "'\\0'"), path); + return -1; + } + + if (VIR_ALLOC_N(passphrase, size + 1) < 0) { + memset(data, 0, size); + VIR_FREE(data); + virReportOOMError(conn); + return -1; + } + memcpy(passphrase, data, size); + passphrase[size] = '\0'; + + memset(data, 0, size); + VIR_FREE(data); + + *secretRet = passphrase; + *secretLen = size; + + return 0; +} + static int qemuConnectMonitor(virDomainObjPtr vm, int reconnect) { @@ -358,6 +465,9 @@ qemuConnectMonitor(virDomainObjPtr vm, int reconnect) return -1; } + qemuMonitorRegisterDiskSecretLookup(priv->mon, + findVolumeQcowPassphrase); + return 0; } diff --git a/src/qemu/qemu_monitor.c b/src/qemu/qemu_monitor.c index 5f7e20c..352328d 100644 --- a/src/qemu/qemu_monitor.c +++ b/src/qemu/qemu_monitor.c @@ -45,6 +45,7 @@ struct _qemuMonitor { virDomainObjPtr vm; qemuMonitorEOFNotify eofCB; + qemuMonitorDiskSecretLookup secretCB; }; /* Return -1 for error, 1 to continue reading and 0 for success */ @@ -322,6 +323,7 @@ qemuMonitorOpen(virDomainObjPtr vm, goto cleanup; } + return mon; cleanup: @@ -344,6 +346,13 @@ void qemuMonitorClose(qemuMonitorPtr mon) } +void qemuMonitorRegisterDiskSecretLookup(qemuMonitorPtr mon, + qemuMonitorDiskSecretLookup secretCB) +{ + mon->secretCB = secretCB; +} + + int qemuMonitorWrite(qemuMonitorPtr mon, const char *data, size_t len) @@ -410,3 +419,16 @@ retry: } return 0; } + + +int qemuMonitorGetDiskSecret(qemuMonitorPtr mon, + virConnectPtr conn, + const char *path, + char **secret, + size_t *secretLen) +{ + *secret = NULL; + *secretLen = 0; + + return mon->secretCB(mon, conn, mon->vm, path, secret, secretLen); +} diff --git a/src/qemu/qemu_monitor.h b/src/qemu/qemu_monitor.h index e863e20..6fb99a9 100644 --- a/src/qemu/qemu_monitor.h +++ b/src/qemu/qemu_monitor.h @@ -36,12 +36,27 @@ typedef void (*qemuMonitorEOFNotify)(qemuMonitorPtr mon, virDomainObjPtr vm, int withError); +/* XXX we'd really like to avoid virCOnnectPtr here + * It is required so the callback can find the active + * secret driver. Need to change this to work like the + * security drivers do, to avoid this + */ +typedef int (*qemuMonitorDiskSecretLookup)(qemuMonitorPtr mon, + virConnectPtr conn, + virDomainObjPtr vm, + const char *path, + char **secret, + size_t *secretLen); + qemuMonitorPtr qemuMonitorOpen(virDomainObjPtr vm, int reconnect, qemuMonitorEOFNotify eofCB); void qemuMonitorClose(qemuMonitorPtr mon); +void qemuMonitorRegisterDiskSecretLookup(qemuMonitorPtr mon, + qemuMonitorDiskSecretLookup secretCB); + int qemuMonitorWrite(qemuMonitorPtr mon, const char *data, size_t len); @@ -57,5 +72,10 @@ int qemuMonitorRead(qemuMonitorPtr mon, int qemuMonitorWaitForInput(qemuMonitorPtr mon); +int qemuMonitorGetDiskSecret(qemuMonitorPtr mon, + virConnectPtr conn, + const char *path, + char **secret, + size_t *secretLen); #endif /* QEMU_MONITOR_H */ diff --git a/src/qemu/qemu_monitor_text.c b/src/qemu/qemu_monitor_text.c index fa17971..a0146ae 100644 --- a/src/qemu/qemu_monitor_text.c +++ b/src/qemu/qemu_monitor_text.c @@ -334,101 +334,6 @@ qemuMonitorCommand(const virDomainObjPtr vm, } - -static virStorageEncryptionPtr -findDomainDiskEncryption(virConnectPtr conn, virDomainObjPtr vm, - const char *path) -{ - bool seen_volume; - int i; - - seen_volume = false; - for (i = 0; i < vm->def->ndisks; i++) { - virDomainDiskDefPtr disk; - - disk = vm->def->disks[i]; - if (disk->src != NULL && STREQ(disk->src, path)) { - seen_volume = true; - if (disk->encryption != NULL) - return disk->encryption; - } - } - if (seen_volume) - qemudReportError(conn, NULL, NULL, VIR_ERR_INVALID_DOMAIN, - _("missing <encryption> for volume %s"), path); - else - qemudReportError(conn, NULL, NULL, VIR_ERR_INTERNAL_ERROR, - _("unexpected passphrase request for volume %s"), - path); - return NULL; -} - -static char * -findVolumeQcowPassphrase(virConnectPtr conn, virDomainObjPtr vm, - const char *path, size_t *passphrase_len) -{ - virStorageEncryptionPtr enc; - virSecretPtr secret; - char *passphrase; - unsigned char *data; - size_t size; - - if (conn->secretDriver == NULL || - conn->secretDriver->lookupByUUID == NULL || - conn->secretDriver->getValue == NULL) { - qemudReportError(conn, NULL, NULL, VIR_ERR_NO_SUPPORT, "%s", - _("secret storage not supported")); - return NULL; - } - - enc = findDomainDiskEncryption(conn, vm, path); - if (enc == NULL) - return NULL; - - if (enc->format != VIR_STORAGE_ENCRYPTION_FORMAT_QCOW || - enc->nsecrets != 1 || - enc->secrets[0]->type != - VIR_STORAGE_ENCRYPTION_SECRET_TYPE_PASSPHRASE) { - qemudReportError(conn, NULL, NULL, VIR_ERR_INVALID_DOMAIN, - _("invalid <encryption> for volume %s"), path); - return NULL; - } - - secret = conn->secretDriver->lookupByUUID(conn, - enc->secrets[0]->uuid); - if (secret == NULL) - return NULL; - data = conn->secretDriver->getValue(secret, &size, - VIR_SECRET_GET_VALUE_INTERNAL_CALL); - virUnrefSecret(secret); - if (data == NULL) - return NULL; - - if (memchr(data, '\0', size) != NULL) { - memset(data, 0, size); - VIR_FREE(data); - qemudReportError(conn, NULL, NULL, VIR_ERR_INVALID_SECRET, - _("format='qcow' passphrase for %s must not contain a " - "'\\0'"), path); - return NULL; - } - - if (VIR_ALLOC_N(passphrase, size + 1) < 0) { - memset(data, 0, size); - VIR_FREE(data); - virReportOOMError(conn); - return NULL; - } - memcpy(passphrase, data, size); - passphrase[size] = '\0'; - - memset(data, 0, size); - VIR_FREE(data); - - *passphrase_len = size; - return passphrase; -} - static int qemuMonitorSendVolumePassphrase(const virDomainObjPtr vm, const char *buf, @@ -436,7 +341,8 @@ qemuMonitorSendVolumePassphrase(const virDomainObjPtr vm, void *data) { virConnectPtr conn = data; - char *passphrase, *path; + qemuDomainObjPrivatePtr priv = vm->privateData; + char *passphrase = NULL, *path; const char *prompt_path; size_t path_len, passphrase_len = 0; int res; @@ -456,9 +362,10 @@ qemuMonitorSendVolumePassphrase(const virDomainObjPtr vm, memcpy(path, prompt_path, path_len); path[path_len] = '\0'; - passphrase = findVolumeQcowPassphrase(conn, vm, path, &passphrase_len); + res = qemuMonitorGetDiskSecret(priv->mon, conn, path, + &passphrase, &passphrase_len); VIR_FREE(path); - if (passphrase == NULL) + if (res < 0) return -1; res = qemuMonitorSend(vm, passphrase, -1); -- 1.6.2.5

On Tue, Nov 03, 2009 at 02:50:00PM -0500, Daniel P. Berrange wrote:
Decouple the monitor code from the virDomainDefPtr structure by moving the disk encryption lookup code back into the qemu_driver.c file. Instead provide a function callback to the monitor code which can be invoked to retrieve encryption data as required.
* src/qemu/qemu_driver.c: Add findDomainDiskEncryption, and findVolumeQcowPassphrase. Pass address of the method findVolumeQcowPassphrase into qemuMonitorOpen() * src/qemu/qemu_monitor.c: Associate a disk encryption function callback with the qemuMonitorPtr object. * src/qemu/qemu_monitor_text.c: Remove findDomainDiskEncryption and findVolumeQcowPassphrase. --- src/qemu/qemu_driver.c | 110 ++++++++++++++++++++++++++++++++++++++++++ src/qemu/qemu_monitor.c | 22 ++++++++ src/qemu/qemu_monitor.h | 20 ++++++++ src/qemu/qemu_monitor_text.c | 103 ++------------------------------------- 4 files changed, 157 insertions(+), 98 deletions(-)
diff --git a/src/qemu/qemu_driver.c b/src/qemu/qemu_driver.c index e985543..30cc66e 100644 --- a/src/qemu/qemu_driver.c +++ b/src/qemu/qemu_driver.c @@ -348,6 +348,113 @@ qemuHandleMonitorEOF(qemuMonitorPtr mon ATTRIBUTE_UNUSED, }
+static virStorageEncryptionPtr +findDomainDiskEncryption(virConnectPtr conn, virDomainObjPtr vm, + const char *path) +{ + bool seen_volume; + int i; + + seen_volume = false; + for (i = 0; i < vm->def->ndisks; i++) { + virDomainDiskDefPtr disk; + + disk = vm->def->disks[i]; + if (disk->src != NULL && STREQ(disk->src, path)) { + seen_volume = true; + if (disk->encryption != NULL) + return disk->encryption; + } + } + if (seen_volume) + qemudReportError(conn, NULL, NULL, VIR_ERR_INVALID_DOMAIN, + _("missing <encryption> for volume %s"), path); + else + qemudReportError(conn, NULL, NULL, VIR_ERR_INTERNAL_ERROR, + _("unexpected passphrase request for volume %s"), + path); + return NULL; +} + + +static int +findVolumeQcowPassphrase(qemuMonitorPtr mon ATTRIBUTE_UNUSED, + virConnectPtr conn, + virDomainObjPtr vm, + const char *path, + char **secretRet, + size_t *secretLen) +{ + virStorageEncryptionPtr enc; + virSecretPtr secret; + char *passphrase; + unsigned char *data; + size_t size; + + if (!conn) { + qemudReportError(NULL, NULL, NULL, VIR_ERR_NO_SUPPORT, + "%s", _("cannot find secrets without a connection")); + return -1; + } + + if (conn->secretDriver == NULL || + conn->secretDriver->lookupByUUID == NULL || + conn->secretDriver->getValue == NULL) { + qemudReportError(conn, NULL, NULL, VIR_ERR_NO_SUPPORT, "%s", + _("secret storage not supported")); + return -1; + } + + enc = findDomainDiskEncryption(conn, vm, path); + if (enc == NULL) + return -1; + + if (enc->format != VIR_STORAGE_ENCRYPTION_FORMAT_QCOW || + enc->nsecrets != 1 || + enc->secrets[0]->type != + VIR_STORAGE_ENCRYPTION_SECRET_TYPE_PASSPHRASE) { + qemudReportError(conn, NULL, NULL, VIR_ERR_INVALID_DOMAIN, + _("invalid <encryption> for volume %s"), path); + return -1; + } + + secret = conn->secretDriver->lookupByUUID(conn, + enc->secrets[0]->uuid); + if (secret == NULL) + return -1; + data = conn->secretDriver->getValue(secret, &size, + VIR_SECRET_GET_VALUE_INTERNAL_CALL); + virUnrefSecret(secret); + if (data == NULL) + return -1; + + if (memchr(data, '\0', size) != NULL) { + memset(data, 0, size); + VIR_FREE(data); + qemudReportError(conn, NULL, NULL, VIR_ERR_INVALID_SECRET, + _("format='qcow' passphrase for %s must not contain a " + "'\\0'"), path); + return -1; + } + + if (VIR_ALLOC_N(passphrase, size + 1) < 0) { + memset(data, 0, size); + VIR_FREE(data); + virReportOOMError(conn); + return -1; + } + memcpy(passphrase, data, size); + passphrase[size] = '\0'; + + memset(data, 0, size); + VIR_FREE(data); + + *secretRet = passphrase; + *secretLen = size; + + return 0; +} + static int qemuConnectMonitor(virDomainObjPtr vm, int reconnect) { @@ -358,6 +465,9 @@ qemuConnectMonitor(virDomainObjPtr vm, int reconnect) return -1; }
+ qemuMonitorRegisterDiskSecretLookup(priv->mon, + findVolumeQcowPassphrase); + return 0; }
diff --git a/src/qemu/qemu_monitor.c b/src/qemu/qemu_monitor.c index 5f7e20c..352328d 100644 --- a/src/qemu/qemu_monitor.c +++ b/src/qemu/qemu_monitor.c @@ -45,6 +45,7 @@ struct _qemuMonitor { virDomainObjPtr vm;
qemuMonitorEOFNotify eofCB; + qemuMonitorDiskSecretLookup secretCB; };
/* Return -1 for error, 1 to continue reading and 0 for success */ @@ -322,6 +323,7 @@ qemuMonitorOpen(virDomainObjPtr vm, goto cleanup; }
+ return mon;
cleanup: @@ -344,6 +346,13 @@ void qemuMonitorClose(qemuMonitorPtr mon) }
+void qemuMonitorRegisterDiskSecretLookup(qemuMonitorPtr mon, + qemuMonitorDiskSecretLookup secretCB) +{ + mon->secretCB = secretCB; +} + + int qemuMonitorWrite(qemuMonitorPtr mon, const char *data, size_t len) @@ -410,3 +419,16 @@ retry: } return 0; } + + +int qemuMonitorGetDiskSecret(qemuMonitorPtr mon, + virConnectPtr conn, + const char *path, + char **secret, + size_t *secretLen) +{ + *secret = NULL; + *secretLen = 0; + + return mon->secretCB(mon, conn, mon->vm, path, secret, secretLen); +} diff --git a/src/qemu/qemu_monitor.h b/src/qemu/qemu_monitor.h index e863e20..6fb99a9 100644 --- a/src/qemu/qemu_monitor.h +++ b/src/qemu/qemu_monitor.h @@ -36,12 +36,27 @@ typedef void (*qemuMonitorEOFNotify)(qemuMonitorPtr mon, virDomainObjPtr vm, int withError);
+/* XXX we'd really like to avoid virCOnnectPtr here + * It is required so the callback can find the active + * secret driver. Need to change this to work like the + * security drivers do, to avoid this + */ +typedef int (*qemuMonitorDiskSecretLookup)(qemuMonitorPtr mon, + virConnectPtr conn, + virDomainObjPtr vm, + const char *path, + char **secret, + size_t *secretLen); + qemuMonitorPtr qemuMonitorOpen(virDomainObjPtr vm, int reconnect, qemuMonitorEOFNotify eofCB);
void qemuMonitorClose(qemuMonitorPtr mon);
+void qemuMonitorRegisterDiskSecretLookup(qemuMonitorPtr mon, + qemuMonitorDiskSecretLookup secretCB); + int qemuMonitorWrite(qemuMonitorPtr mon, const char *data, size_t len); @@ -57,5 +72,10 @@ int qemuMonitorRead(qemuMonitorPtr mon,
int qemuMonitorWaitForInput(qemuMonitorPtr mon);
+int qemuMonitorGetDiskSecret(qemuMonitorPtr mon, + virConnectPtr conn, + const char *path, + char **secret, + size_t *secretLen);
#endif /* QEMU_MONITOR_H */ diff --git a/src/qemu/qemu_monitor_text.c b/src/qemu/qemu_monitor_text.c index fa17971..a0146ae 100644 --- a/src/qemu/qemu_monitor_text.c +++ b/src/qemu/qemu_monitor_text.c @@ -334,101 +334,6 @@ qemuMonitorCommand(const virDomainObjPtr vm, }
- -static virStorageEncryptionPtr -findDomainDiskEncryption(virConnectPtr conn, virDomainObjPtr vm, - const char *path) -{ - bool seen_volume; - int i; - - seen_volume = false; - for (i = 0; i < vm->def->ndisks; i++) { - virDomainDiskDefPtr disk; - - disk = vm->def->disks[i]; - if (disk->src != NULL && STREQ(disk->src, path)) { - seen_volume = true; - if (disk->encryption != NULL) - return disk->encryption; - } - } - if (seen_volume) - qemudReportError(conn, NULL, NULL, VIR_ERR_INVALID_DOMAIN, - _("missing <encryption> for volume %s"), path); - else - qemudReportError(conn, NULL, NULL, VIR_ERR_INTERNAL_ERROR, - _("unexpected passphrase request for volume %s"), - path); - return NULL; -} - -static char * -findVolumeQcowPassphrase(virConnectPtr conn, virDomainObjPtr vm, - const char *path, size_t *passphrase_len) -{ - virStorageEncryptionPtr enc; - virSecretPtr secret; - char *passphrase; - unsigned char *data; - size_t size; - - if (conn->secretDriver == NULL || - conn->secretDriver->lookupByUUID == NULL || - conn->secretDriver->getValue == NULL) { - qemudReportError(conn, NULL, NULL, VIR_ERR_NO_SUPPORT, "%s", - _("secret storage not supported")); - return NULL; - } - - enc = findDomainDiskEncryption(conn, vm, path); - if (enc == NULL) - return NULL; - - if (enc->format != VIR_STORAGE_ENCRYPTION_FORMAT_QCOW || - enc->nsecrets != 1 || - enc->secrets[0]->type != - VIR_STORAGE_ENCRYPTION_SECRET_TYPE_PASSPHRASE) { - qemudReportError(conn, NULL, NULL, VIR_ERR_INVALID_DOMAIN, - _("invalid <encryption> for volume %s"), path); - return NULL; - } - - secret = conn->secretDriver->lookupByUUID(conn, - enc->secrets[0]->uuid); - if (secret == NULL) - return NULL; - data = conn->secretDriver->getValue(secret, &size, - VIR_SECRET_GET_VALUE_INTERNAL_CALL); - virUnrefSecret(secret); - if (data == NULL) - return NULL; - - if (memchr(data, '\0', size) != NULL) { - memset(data, 0, size); - VIR_FREE(data); - qemudReportError(conn, NULL, NULL, VIR_ERR_INVALID_SECRET, - _("format='qcow' passphrase for %s must not contain a " - "'\\0'"), path); - return NULL; - } - - if (VIR_ALLOC_N(passphrase, size + 1) < 0) { - memset(data, 0, size); - VIR_FREE(data); - virReportOOMError(conn); - return NULL; - } - memcpy(passphrase, data, size); - passphrase[size] = '\0'; - - memset(data, 0, size); - VIR_FREE(data); - - *passphrase_len = size; - return passphrase; -} - static int qemuMonitorSendVolumePassphrase(const virDomainObjPtr vm, const char *buf, @@ -436,7 +341,8 @@ qemuMonitorSendVolumePassphrase(const virDomainObjPtr vm, void *data) { virConnectPtr conn = data; - char *passphrase, *path; + qemuDomainObjPrivatePtr priv = vm->privateData; + char *passphrase = NULL, *path; const char *prompt_path; size_t path_len, passphrase_len = 0; int res; @@ -456,9 +362,10 @@ qemuMonitorSendVolumePassphrase(const virDomainObjPtr vm, memcpy(path, prompt_path, path_len); path[path_len] = '\0';
- passphrase = findVolumeQcowPassphrase(conn, vm, path, &passphrase_len); + res = qemuMonitorGetDiskSecret(priv->mon, conn, path, + &passphrase, &passphrase_len); VIR_FREE(path); - if (passphrase == NULL) + if (res < 0) return -1;
res = qemuMonitorSend(vm, passphrase, -1);
ACK, Daniel -- Daniel Veillard | libxml Gnome XML XSLT toolkit http://xmlsoft.org/ daniel@veillard.com | Rpmfind RPM search engine http://rpmfind.net/ http://veillard.com/ | virtualization library http://libvirt.org/

Change the QEMU driver to not directly invoke the text mode monitor APIs. Instead add a generic wrapper layer, which will eventually invoke either the text or JSON protocol code as needed. Pass an qemuMonitorPtr object into the monitor APIs instead of virDomainObjPtr to complete the de-coupling of the monitor impl from virDomainObj data structures * src/qemu/qemu_conf.h: Remove qemuDomainObjPrivate definition * src/qemu/qemu_driver.c: Add qemuDomainObjPrivate definition. Pass qemuMonitorPtr into all monitor APIs instead of the virDomainObjPtr instance. * src/qemu/qemu_monitor.c, src/qemu/qemu_monitor.h: Add thin wrappers for all qemuMonitorXXX command APIs, calling into qemu_monitor_text.c/h * src/qemu/qemu_monitor_text.c, src/qemu/qemu_monitor_text.h: Rename qemuMonitor -> qemuMonitorText & update to accept qemuMonitorPtr instead of virDomainObjPtr --- src/qemu/qemu_conf.h | 11 - src/qemu/qemu_driver.c | 133 +++++++++----- src/qemu/qemu_monitor.c | 319 ++++++++++++++++++++++++++++++ src/qemu/qemu_monitor.h | 148 ++++++++++++++ src/qemu/qemu_monitor_text.c | 436 +++++++++++++++++------------------------- src/qemu/qemu_monitor_text.h | 269 ++++++++++++--------------- 6 files changed, 845 insertions(+), 471 deletions(-) diff --git a/src/qemu/qemu_conf.h b/src/qemu/qemu_conf.h index 171bf90..f9a970f 100644 --- a/src/qemu/qemu_conf.h +++ b/src/qemu/qemu_conf.h @@ -36,7 +36,6 @@ #include "security/security_driver.h" #include "cgroup.h" #include "pci.h" -#include "qemu_monitor.h" #define qemudDebug(fmt, ...) do {} while(0) @@ -129,16 +128,6 @@ struct qemud_driver { pciDeviceList *activePciHostdevs; }; -/* XXX temporarily exposed. - * This will be moved back into qemu_driver.c, once the - * qemu_monitor* code is refactored a little more - */ -typedef struct _qemuDomainObjPrivate qemuDomainObjPrivate; -typedef qemuDomainObjPrivate *qemuDomainObjPrivatePtr; -struct _qemuDomainObjPrivate { - qemuMonitorPtr mon; -}; - /* Port numbers used for KVM migration. */ #define QEMUD_MIGRATION_FIRST_PORT 49152 diff --git a/src/qemu/qemu_driver.c b/src/qemu/qemu_driver.c index 30cc66e..0fbd20f 100644 --- a/src/qemu/qemu_driver.c +++ b/src/qemu/qemu_driver.c @@ -56,7 +56,6 @@ #include "qemu_driver.h" #include "qemu_conf.h" #include "qemu_monitor.h" -#include "qemu_monitor_text.h" #include "c-ctype.h" #include "event.h" #include "buf.h" @@ -77,6 +76,12 @@ #define VIR_FROM_THIS VIR_FROM_QEMU +typedef struct _qemuDomainObjPrivate qemuDomainObjPrivate; +typedef qemuDomainObjPrivate *qemuDomainObjPrivatePtr; +struct _qemuDomainObjPrivate { + qemuMonitorPtr mon; +}; + static int qemudShutdown(void); static void qemuDriverLock(struct qemud_driver *driver) @@ -1120,6 +1125,7 @@ qemuDetectVcpuPIDs(virConnectPtr conn, virDomainObjPtr vm) { pid_t *cpupids = NULL; int ncpupids; + qemuDomainObjPrivatePtr priv = vm->privateData; if (vm->def->virtType != VIR_DOMAIN_VIRT_KVM) { vm->nvcpupids = 1; @@ -1133,7 +1139,7 @@ qemuDetectVcpuPIDs(virConnectPtr conn, /* What follows is now all KVM specific */ - if ((ncpupids = qemuMonitorGetCPUInfo(vm, &cpupids)) < 0) + if ((ncpupids = qemuMonitorGetCPUInfo(priv->mon, &cpupids)) < 0) return -1; /* Treat failure to get VCPU<->PID mapping as non-fatal */ @@ -1161,6 +1167,7 @@ qemudInitCpus(virConnectPtr conn, cpu_set_t mask; int i, maxcpu = QEMUD_CPUMASK_LEN; virNodeInfo nodeinfo; + qemuDomainObjPrivatePtr priv = vm->privateData; if (nodeGetInfo(conn, &nodeinfo) < 0) return -1; @@ -1192,7 +1199,7 @@ qemudInitCpus(virConnectPtr conn, if (migrateFrom == NULL) { /* Allow the CPUS to start executing */ - if (qemuMonitorStartCPUs(conn, vm) < 0) { + if (qemuMonitorStartCPUs(priv->mon, conn) < 0) { if (virGetLastError() == NULL) qemudReportError(conn, NULL, NULL, VIR_ERR_INTERNAL_ERROR, "%s", _("resume operation failed")); @@ -1208,12 +1215,13 @@ static int qemuInitPasswords(struct qemud_driver *driver, virDomainObjPtr vm) { int ret = 0; + qemuDomainObjPrivatePtr priv = vm->privateData; if ((vm->def->ngraphics == 1) && vm->def->graphics[0]->type == VIR_DOMAIN_GRAPHICS_TYPE_VNC && (vm->def->graphics[0]->data.vnc.passwd || driver->vncPassword)) { - ret = qemuMonitorSetVNCPassword(vm, + ret = qemuMonitorSetVNCPassword(priv->mon, vm->def->graphics[0]->data.vnc.passwd ? vm->def->graphics[0]->data.vnc.passwd : driver->vncPassword); @@ -2081,7 +2089,7 @@ static int qemudStartVMDaemon(virConnectPtr conn, (qemuDetectVcpuPIDs(conn, vm) < 0) || (qemudInitCpus(conn, vm, migrateFrom) < 0) || (qemuInitPasswords(driver, vm) < 0) || - (qemuMonitorSetBalloon(vm, vm->def->memory) < 0) || + (qemuMonitorSetBalloon(((qemuDomainObjPrivatePtr)vm->privateData)->mon, vm->def->memory) < 0) || (virDomainSaveStatus(conn, driver->stateDir, vm) < 0)) { qemudShutdownVMDaemon(conn, driver, vm); ret = -1; @@ -2640,7 +2648,8 @@ static int qemudDomainSuspend(virDomainPtr dom) { goto cleanup; } if (vm->state != VIR_DOMAIN_PAUSED) { - if (qemuMonitorStopCPUs(vm) < 0) + qemuDomainObjPrivatePtr priv = vm->privateData; + if (qemuMonitorStopCPUs(priv->mon) < 0) goto cleanup; vm->state = VIR_DOMAIN_PAUSED; event = virDomainEventNewFromObj(vm, @@ -2684,7 +2693,8 @@ static int qemudDomainResume(virDomainPtr dom) { goto cleanup; } if (vm->state == VIR_DOMAIN_PAUSED) { - if (qemuMonitorStartCPUs(dom->conn, vm) < 0) { + qemuDomainObjPrivatePtr priv = vm->privateData; + if (qemuMonitorStartCPUs(priv->mon, dom->conn) < 0) { if (virGetLastError() == NULL) qemudReportError(dom->conn, dom, NULL, VIR_ERR_OPERATION_FAILED, "%s", _("resume operation failed")); @@ -2732,7 +2742,8 @@ static int qemudDomainShutdown(virDomainPtr dom) { goto cleanup; } - if (qemuMonitorSystemPowerdown(vm) < 0) + qemuDomainObjPrivatePtr priv = vm->privateData; + if (qemuMonitorSystemPowerdown(priv->mon) < 0) goto cleanup; ret = 0; @@ -2893,7 +2904,8 @@ static int qemudDomainSetMemory(virDomainPtr dom, unsigned long newmem) { } if (virDomainObjIsActive(vm)) { - int r = qemuMonitorSetBalloon(vm, newmem); + qemuDomainObjPrivatePtr priv = vm->privateData; + int r = qemuMonitorSetBalloon(priv->mon, newmem); if (r < 0) goto cleanup; @@ -2947,7 +2959,8 @@ static int qemudDomainGetInfo(virDomainPtr dom, info->maxMem = vm->def->maxmem; if (virDomainObjIsActive(vm)) { - err = qemuMonitorGetBalloonInfo(vm, &balloon); + qemuDomainObjPrivatePtr priv = vm->privateData; + err = qemuMonitorGetBalloonInfo(priv->mon, &balloon); if (err < 0) goto cleanup; @@ -3054,8 +3067,9 @@ static int qemudDomainSave(virDomainPtr dom, /* Pause */ if (vm->state == VIR_DOMAIN_RUNNING) { + qemuDomainObjPrivatePtr priv = vm->privateData; header.was_running = 1; - if (qemuMonitorStopCPUs(vm) < 0) + if (qemuMonitorStopCPUs(priv->mon) < 0) goto cleanup; vm->state = VIR_DOMAIN_PAUSED; } @@ -3098,15 +3112,17 @@ static int qemudDomainSave(virDomainPtr dom, if (header.compressed == QEMUD_SAVE_FORMAT_RAW) { const char *args[] = { "cat", NULL }; - ret = qemuMonitorMigrateToCommand(vm, 0, args, path); + qemuDomainObjPrivatePtr priv = vm->privateData; + ret = qemuMonitorMigrateToCommand(priv->mon, 0, args, path); } else { const char *prog = qemudSaveCompressionTypeToString(header.compressed); + qemuDomainObjPrivatePtr priv = vm->privateData; const char *args[] = { prog, "-c", NULL }; - ret = qemuMonitorMigrateToCommand(vm, 0, args, path); + ret = qemuMonitorMigrateToCommand(priv->mon, 0, args, path); } if (ret < 0) @@ -3173,14 +3189,16 @@ static int qemudDomainCoreDump(virDomainPtr dom, the stop command is issued. */ resume = (vm->state == VIR_DOMAIN_RUNNING); + qemuDomainObjPrivatePtr priv = vm->privateData; + /* Pause domain for non-live dump */ if (vm->state == VIR_DOMAIN_RUNNING) { - if (qemuMonitorStopCPUs(vm) < 0) + if (qemuMonitorStopCPUs(priv->mon) < 0) goto cleanup; paused = 1; } - ret = qemuMonitorMigrateToCommand(vm, 0, args, path); + ret = qemuMonitorMigrateToCommand(priv->mon, 0, args, path); paused = 1; cleanup: @@ -3188,7 +3206,7 @@ cleanup: will support synchronous operations so we always get here after the migration is complete. */ if (resume && paused) { - if (qemuMonitorStartCPUs(dom->conn, vm) < 0) { + if (qemuMonitorStartCPUs(priv->mon, dom->conn) < 0) { if (virGetLastError() == NULL) qemudReportError(dom->conn, dom, NULL, VIR_ERR_OPERATION_FAILED, "%s", _("resuming after dump failed")); @@ -3705,7 +3723,8 @@ static int qemudDomainRestore(virConnectPtr conn, /* If it was running before, resume it now. */ if (header.was_running) { - if (qemuMonitorStartCPUs(conn, vm) < 0) { + qemuDomainObjPrivatePtr priv = vm->privateData; + if (qemuMonitorStartCPUs(priv->mon, conn) < 0) { if (virGetLastError() == NULL) qemudReportError(conn, NULL, NULL, VIR_ERR_OPERATION_FAILED, "%s", _("failed to resume domain")); @@ -3752,7 +3771,8 @@ static char *qemudDomainDumpXML(virDomainPtr dom, /* Refresh current memory based on balloon info */ if (virDomainObjIsActive(vm)) { - err = qemuMonitorGetBalloonInfo(vm, &balloon); + qemuDomainObjPrivatePtr priv = vm->privateData; + err = qemuMonitorGetBalloonInfo(priv->mon, &balloon); if (err < 0) goto cleanup; if (err > 0) @@ -4322,10 +4342,11 @@ static int qemudDomainChangeEjectableMedia(virConnectPtr conn, } } + qemuDomainObjPrivatePtr priv = vm->privateData; if (newdisk->src) { - ret = qemuMonitorChangeMedia(vm, devname, newdisk->src); + ret = qemuMonitorChangeMedia(priv->mon, devname, newdisk->src); } else { - ret = qemuMonitorEjectMedia(vm, devname); + ret = qemuMonitorEjectMedia(priv->mon, devname); } if (ret == 0) { @@ -4345,6 +4366,7 @@ static int qemudDomainAttachPciDiskDevice(virConnectPtr conn, { int i; const char* type = virDomainDiskBusTypeToString(dev->data.disk->bus); + qemuDomainObjPrivatePtr priv = vm->privateData; for (i = 0 ; i < vm->def->ndisks ; i++) { if (STREQ(vm->def->disks[i]->dst, dev->data.disk->dst)) { @@ -4359,7 +4381,7 @@ static int qemudDomainAttachPciDiskDevice(virConnectPtr conn, return -1; } - if (qemuMonitorAddPCIDisk(vm, + if (qemuMonitorAddPCIDisk(priv->mon, dev->data.disk->src, type, &dev->data.disk->pci_addr.domain, @@ -4376,6 +4398,7 @@ static int qemudDomainAttachUsbMassstorageDevice(virConnectPtr conn, virDomainObjPtr vm, virDomainDeviceDefPtr dev) { + qemuDomainObjPrivatePtr priv = vm->privateData; int i; for (i = 0 ; i < vm->def->ndisks ; i++) { @@ -4397,7 +4420,7 @@ static int qemudDomainAttachUsbMassstorageDevice(virConnectPtr conn, return -1; } - if (qemuMonitorAddUSBDisk(vm, dev->data.disk->src) < 0) + if (qemuMonitorAddUSBDisk(priv->mon, dev->data.disk->src) < 0) return -1; virDomainDiskInsertPreAlloced(vm->def, dev->data.disk); @@ -4412,6 +4435,7 @@ static int qemudDomainAttachNetDevice(virConnectPtr conn, unsigned int qemuCmdFlags) { virDomainNetDefPtr net = dev->data.net; + qemuDomainObjPrivatePtr priv = vm->privateData; char *tapfd_name = NULL; int i, tapfd = -1; char *nicstr = NULL; @@ -4457,7 +4481,7 @@ static int qemudDomainAttachNetDevice(virConnectPtr conn, if (virAsprintf(&tapfd_name, "fd-%s", net->hostnet_name) < 0) goto no_memory; - if (qemuMonitorSendFileHandle(vm, tapfd_name, tapfd) < 0) + if (qemuMonitorSendFileHandle(priv->mon, tapfd_name, tapfd) < 0) goto cleanup; } @@ -4465,7 +4489,7 @@ static int qemudDomainAttachNetDevice(virConnectPtr conn, net->vlan, tapfd_name, &netstr) < 0) goto try_tapfd_close; - if (qemuMonitorAddHostNetwork(vm, netstr) < 0) + if (qemuMonitorAddHostNetwork(priv->mon, netstr) < 0) goto try_tapfd_close; if (tapfd != -1) @@ -4475,7 +4499,7 @@ static int qemudDomainAttachNetDevice(virConnectPtr conn, if (qemuBuildNicStr(conn, net, NULL, net->vlan, &nicstr) < 0) goto try_remove; - if (qemuMonitorAddPCINetwork(vm, nicstr, + if (qemuMonitorAddPCINetwork(priv->mon, nicstr, &net->pci_addr.domain, &net->pci_addr.bus, &net->pci_addr.slot) < 0) @@ -4497,14 +4521,14 @@ cleanup: try_remove: if (!net->hostnet_name || net->vlan == 0) VIR_WARN0(_("Unable to remove network backend\n")); - else if (qemuMonitorRemoveHostNetwork(vm, net->vlan, net->hostnet_name) < 0) + else if (qemuMonitorRemoveHostNetwork(priv->mon, net->vlan, net->hostnet_name) < 0) VIR_WARN(_("Failed to remove network backend for vlan %d, net %s"), net->vlan, net->hostnet_name); goto cleanup; try_tapfd_close: if (tapfd_name && - qemuMonitorCloseFileHandle(vm, tapfd_name) < 0) + qemuMonitorCloseFileHandle(priv->mon, tapfd_name) < 0) VIR_WARN(_("Failed to close tapfd with '%s'\n"), tapfd_name); goto cleanup; @@ -4519,6 +4543,7 @@ static int qemudDomainAttachHostPciDevice(virConnectPtr conn, virDomainObjPtr vm, virDomainDeviceDefPtr dev) { + qemuDomainObjPrivatePtr priv = vm->privateData; virDomainHostdevDefPtr hostdev = dev->data.hostdev; pciDevice *pci; @@ -4546,7 +4571,7 @@ static int qemudDomainAttachHostPciDevice(virConnectPtr conn, return -1; } - if (qemuMonitorAddPCIHostDevice(vm, + if (qemuMonitorAddPCIHostDevice(priv->mon, hostdev->source.subsys.u.pci.domain, hostdev->source.subsys.u.pci.bus, hostdev->source.subsys.u.pci.slot, @@ -4571,6 +4596,7 @@ static int qemudDomainAttachHostUsbDevice(virConnectPtr conn, virDomainDeviceDefPtr dev) { int ret; + qemuDomainObjPrivatePtr priv = vm->privateData; if (VIR_REALLOC_N(vm->def->hostdevs, vm->def->nhostdevs+1) < 0) { virReportOOMError(conn); @@ -4578,11 +4604,11 @@ static int qemudDomainAttachHostUsbDevice(virConnectPtr conn, } if (dev->data.hostdev->source.subsys.u.usb.vendor) { - ret = qemuMonitorAddUSBDeviceMatch(vm, + ret = qemuMonitorAddUSBDeviceMatch(priv->mon, dev->data.hostdev->source.subsys.u.usb.vendor, dev->data.hostdev->source.subsys.u.usb.product); } else { - ret = qemuMonitorAddUSBDeviceExact(vm, + ret = qemuMonitorAddUSBDeviceExact(priv->mon, dev->data.hostdev->source.subsys.u.usb.bus, dev->data.hostdev->source.subsys.u.usb.device); } @@ -4756,6 +4782,7 @@ static int qemudDomainDetachPciDiskDevice(virConnectPtr conn, { int i, ret = -1; virDomainDiskDefPtr detach = NULL; + qemuDomainObjPrivatePtr priv = vm->privateData; for (i = 0 ; i < vm->def->ndisks ; i++) { if (STREQ(vm->def->disks[i]->dst, dev->data.disk->dst)) { @@ -4777,7 +4804,7 @@ static int qemudDomainDetachPciDiskDevice(virConnectPtr conn, goto cleanup; } - if (qemuMonitorRemovePCIDevice(vm, + if (qemuMonitorRemovePCIDevice(priv->mon, detach->pci_addr.domain, detach->pci_addr.bus, detach->pci_addr.slot) < 0) @@ -4811,6 +4838,7 @@ qemudDomainDetachNetDevice(virConnectPtr conn, { int i, ret = -1; virDomainNetDefPtr detach = NULL; + qemuDomainObjPrivatePtr priv = vm->privateData; for (i = 0 ; i < vm->def->nnets ; i++) { virDomainNetDefPtr net = vm->def->nets[i]; @@ -4836,13 +4864,13 @@ qemudDomainDetachNetDevice(virConnectPtr conn, goto cleanup; } - if (qemuMonitorRemovePCIDevice(vm, + if (qemuMonitorRemovePCIDevice(priv->mon, detach->pci_addr.domain, detach->pci_addr.bus, detach->pci_addr.slot) < 0) goto cleanup; - if (qemuMonitorRemoveHostNetwork(vm, detach->vlan, detach->hostnet_name) < 0) + if (qemuMonitorRemoveHostNetwork(priv->mon, detach->vlan, detach->hostnet_name) < 0) goto cleanup; if (vm->def->nnets > 1) { @@ -4872,6 +4900,7 @@ static int qemudDomainDetachHostPciDevice(virConnectPtr conn, virDomainDeviceDefPtr dev) { virDomainHostdevDefPtr detach = NULL; + qemuDomainObjPrivatePtr priv = vm->privateData; int i, ret; pciDevice *pci; @@ -4906,7 +4935,7 @@ static int qemudDomainDetachHostPciDevice(virConnectPtr conn, return -1; } - if (qemuMonitorRemovePCIDevice(vm, + if (qemuMonitorRemovePCIDevice(priv->mon, detach->source.subsys.u.pci.guest_addr.domain, detach->source.subsys.u.pci.guest_addr.bus, detach->source.subsys.u.pci.guest_addr.slot) < 0) @@ -5339,7 +5368,9 @@ qemudDomainBlockStats (virDomainPtr dom, if (!qemu_dev_name) goto cleanup; - if (qemuMonitorGetBlockStatsInfo(vm, qemu_dev_name, + qemuDomainObjPrivatePtr priv = vm->privateData; + if (qemuMonitorGetBlockStatsInfo(priv->mon, + qemu_dev_name, &stats->rd_req, &stats->rd_bytes, &stats->wr_req, @@ -5536,11 +5567,12 @@ qemudDomainMemoryPeek (virDomainPtr dom, goto cleanup; } + qemuDomainObjPrivatePtr priv = vm->privateData; if (flags == VIR_MEMORY_VIRTUAL) { - if (qemuMonitorSaveVirtualMemory(vm, offset, size, tmp) < 0) + if (qemuMonitorSaveVirtualMemory(priv->mon, offset, size, tmp) < 0) goto cleanup; } else { - if (qemuMonitorSavePhysicalMemory(vm, offset, size, tmp) < 0) + if (qemuMonitorSavePhysicalMemory(priv->mon, offset, size, tmp) < 0) goto cleanup; } @@ -6293,6 +6325,7 @@ static int doNativeMigrate(virDomainPtr dom, xmlURIPtr uribits = NULL; int status; unsigned long long transferred, remaining, total; + qemuDomainObjPrivatePtr priv = vm->privateData; /* Issue the migrate command. */ if (STRPREFIX(uri, "tcp:") && !STRPREFIX(uri, "tcp://")) { @@ -6314,16 +6347,17 @@ static int doNativeMigrate(virDomainPtr dom, } if (resource > 0 && - qemuMonitorSetMigrationSpeed(vm, resource) < 0) + qemuMonitorSetMigrationSpeed(priv->mon, resource) < 0) goto cleanup; - if (qemuMonitorMigrateToHost(vm, 0, uribits->server, uribits->port) < 0) + if (qemuMonitorMigrateToHost(priv->mon, 0, uribits->server, uribits->port) < 0) goto cleanup; /* it is also possible that the migrate didn't fail initially, but * rather failed later on. Check the output of "info migrate" */ - if (qemuMonitorGetMigrationStatus(vm, &status, + if (qemuMonitorGetMigrationStatus(priv->mon, + &status, &transferred, &remaining, &total) < 0) { @@ -6388,6 +6422,7 @@ static int doTunnelMigrate(virDomainPtr dom, unsigned long resource) { struct qemud_driver *driver = dom->conn->privateData; + qemuDomainObjPrivatePtr priv = vm->privateData; int client_sock = -1; int qemu_sock = -1; struct sockaddr_un sa_qemu, sa_client; @@ -6485,10 +6520,10 @@ static int doTunnelMigrate(virDomainPtr dom, /* 3. start migration on source */ if (qemuCmdFlags & QEMUD_CMD_FLAG_MIGRATE_QEMU_UNIX) - internalret = qemuMonitorMigrateToUnix(vm, 1, unixfile); + internalret = qemuMonitorMigrateToUnix(priv->mon, 1, unixfile); else if (qemuCmdFlags & QEMUD_CMD_FLAG_MIGRATE_QEMU_EXEC) { const char *args[] = { "nc", "-U", unixfile, NULL }; - internalret = qemuMonitorMigrateToCommand(vm, 1, args, "/dev/null"); + internalret = qemuMonitorMigrateToCommand(priv->mon, 1, args, "/dev/null"); } else { internalret = -1; } @@ -6504,7 +6539,8 @@ static int doTunnelMigrate(virDomainPtr dom, /* it is also possible that the migrate didn't fail initially, but * rather failed later on. Check the output of "info migrate" */ - if (qemuMonitorGetMigrationStatus(vm, &status, + if (qemuMonitorGetMigrationStatus(priv->mon, + &status, &transferred, &remaining, &total) < 0) { @@ -6530,7 +6566,7 @@ static int doTunnelMigrate(virDomainPtr dom, cancel: if (retval != 0) - qemuMonitorMigrateCancel(vm); + qemuMonitorMigrateCancel(priv->mon); finish: dname = dname ? dname : dom->name; @@ -6691,8 +6727,9 @@ qemudDomainMigratePerform (virDomainPtr dom, } if (!(flags & VIR_MIGRATE_LIVE)) { + qemuDomainObjPrivatePtr priv = vm->privateData; /* Pause domain for non-live migration */ - if (qemuMonitorStopCPUs(vm) < 0) + if (qemuMonitorStopCPUs(priv->mon) < 0) goto cleanup; paused = 1; @@ -6729,8 +6766,9 @@ qemudDomainMigratePerform (virDomainPtr dom, cleanup: if (paused) { + qemuDomainObjPrivatePtr priv = vm->privateData; /* we got here through some sort of failure; start the domain again */ - if (qemuMonitorStartCPUs(dom->conn, vm) < 0) { + if (qemuMonitorStartCPUs(priv->mon, dom->conn) < 0) { /* Hm, we already know we are in error here. We don't want to * overwrite the previous error, though, so we just throw something * to the logs and hope for the best @@ -6808,13 +6846,14 @@ qemudDomainMigrateFinish2 (virConnectPtr dconn, qemuDomainEventQueue(driver, event); } + qemuDomainObjPrivatePtr priv = vm->privateData; dom = virGetDomain (dconn, vm->def->name, vm->def->uuid); /* run 'cont' on the destination, which allows migration on qemu * >= 0.10.6 to work properly. This isn't strictly necessary on * older qemu's, but it also doesn't hurt anything there */ - if (qemuMonitorStartCPUs(dconn, vm) < 0) { + if (qemuMonitorStartCPUs(priv->mon, dconn) < 0) { if (virGetLastError() == NULL) qemudReportError(dconn, NULL, NULL, VIR_ERR_INTERNAL_ERROR, "%s", _("resume operation failed")); diff --git a/src/qemu/qemu_monitor.c b/src/qemu/qemu_monitor.c index 352328d..46d5d9f 100644 --- a/src/qemu/qemu_monitor.c +++ b/src/qemu/qemu_monitor.c @@ -29,6 +29,7 @@ #include <fcntl.h> #include "qemu_monitor.h" +#include "qemu_monitor_text.h" #include "qemu_conf.h" #include "event.h" #include "virterror_internal.h" @@ -432,3 +433,321 @@ int qemuMonitorGetDiskSecret(qemuMonitorPtr mon, return mon->secretCB(mon, conn, mon->vm, path, secret, secretLen); } + + +int +qemuMonitorStartCPUs(qemuMonitorPtr mon, + virConnectPtr conn) +{ + DEBUG("mon=%p, fd=%d", mon, mon->fd); + + return qemuMonitorTextStartCPUs(mon, conn); +} + + +int +qemuMonitorStopCPUs(qemuMonitorPtr mon) +{ + DEBUG("mon=%p, fd=%d", mon, mon->fd); + + return qemuMonitorTextStopCPUs(mon); +} + + +int qemuMonitorSystemPowerdown(qemuMonitorPtr mon) +{ + DEBUG("mon=%p, fd=%d", mon, mon->fd); + + return qemuMonitorTextSystemPowerdown(mon); +} + + +int qemuMonitorGetCPUInfo(qemuMonitorPtr mon, + int **pids) +{ + DEBUG("mon=%p, fd=%d", mon, mon->fd); + + return qemuMonitorTextGetCPUInfo(mon, pids); +} + +int qemuMonitorGetBalloonInfo(qemuMonitorPtr mon, + unsigned long *currmem) +{ + DEBUG("mon=%p, fd=%d", mon, mon->fd); + + return qemuMonitorTextGetBalloonInfo(mon, currmem); +} + + +int qemuMonitorGetBlockStatsInfo(qemuMonitorPtr mon, + const char *devname, + long long *rd_req, + long long *rd_bytes, + long long *wr_req, + long long *wr_bytes, + long long *errs) +{ + DEBUG("mon=%p, fd=%d dev=%s", mon, mon->fd, devname); + + return qemuMonitorTextGetBlockStatsInfo(mon, devname, + rd_req, rd_bytes, + wr_req, wr_bytes, + errs); +} + + +int qemuMonitorSetVNCPassword(qemuMonitorPtr mon, + const char *password) +{ + DEBUG("mon=%p, fd=%d", mon, mon->fd); + + return qemuMonitorTextSetVNCPassword(mon, password); +} + + +int qemuMonitorSetBalloon(qemuMonitorPtr mon, + unsigned long newmem) +{ + DEBUG("mon=%p, fd=%d newmem=%lu", mon, mon->fd, newmem); + + return qemuMonitorTextSetBalloon(mon, newmem); +} + +int qemuMonitorEjectMedia(qemuMonitorPtr mon, + const char *devname) +{ + DEBUG("mon=%p, fd=%d devname=%s", mon, mon->fd, devname); + + return qemuMonitorTextEjectMedia(mon, devname); +} + + +int qemuMonitorChangeMedia(qemuMonitorPtr mon, + const char *devname, + const char *newmedia) +{ + DEBUG("mon=%p, fd=%d devname=%s newmedia=%s", + mon, mon->fd, devname, newmedia); + + return qemuMonitorTextChangeMedia(mon, devname, newmedia); +} + + +int qemuMonitorSaveVirtualMemory(qemuMonitorPtr mon, + unsigned long long offset, + size_t length, + const char *path) +{ + DEBUG("mon=%p, fd=%d offset=%llu length=%zu path=%s", + mon, mon->fd, offset, length, path); + + return qemuMonitorTextSaveVirtualMemory(mon, offset, length, path); +} + +int qemuMonitorSavePhysicalMemory(qemuMonitorPtr mon, + unsigned long long offset, + size_t length, + const char *path) +{ + DEBUG("mon=%p, fd=%d offset=%llu length=%zu path=%s", + mon, mon->fd, offset, length, path); + + return qemuMonitorTextSavePhysicalMemory(mon, offset, length, path); +} + + +int qemuMonitorSetMigrationSpeed(qemuMonitorPtr mon, + unsigned long bandwidth) +{ + DEBUG("mon=%p, fd=%d bandwidth=%lu", mon, mon->fd, bandwidth); + + return qemuMonitorTextSetMigrationSpeed(mon, bandwidth); +} + +int qemuMonitorGetMigrationStatus(qemuMonitorPtr mon, + int *status, + unsigned long long *transferred, + unsigned long long *remaining, + unsigned long long *total) +{ + DEBUG("mon=%p, fd=%d", mon, mon->fd); + + return qemuMonitorTextGetMigrationStatus(mon, status, + transferred, + remaining, + total); +} + + +int qemuMonitorMigrateToHost(qemuMonitorPtr mon, + int background, + const char *hostname, + int port) +{ + DEBUG("mon=%p, fd=%d hostname=%s port=%d", + mon, mon->fd, hostname, port); + + return qemuMonitorTextMigrateToHost(mon, background, hostname, port); +} + + +int qemuMonitorMigrateToCommand(qemuMonitorPtr mon, + int background, + const char * const *argv, + const char *target) +{ + DEBUG("mon=%p, fd=%d argv=%p target=%s", + mon, mon->fd, argv, target); + + return qemuMonitorTextMigrateToCommand(mon, background, argv, target); +} + +int qemuMonitorMigrateToUnix(qemuMonitorPtr mon, + int background, + const char *unixfile) +{ + DEBUG("mon=%p fd=%d unixfile=%s", + mon, mon->fd, unixfile); + + return qemuMonitorTextMigrateToUnix(mon, background, unixfile); +} + +int qemuMonitorMigrateCancel(qemuMonitorPtr mon) +{ + DEBUG("mon=%p fd=%d", mon, mon->fd); + + return qemuMonitorTextMigrateCancel(mon); +} + +int qemuMonitorAddUSBDisk(qemuMonitorPtr mon, + const char *path) +{ + DEBUG("mon=%p, fd=%d path=%s", mon, mon->fd, path); + + return qemuMonitorTextAddUSBDisk(mon, path); +} + + +int qemuMonitorAddUSBDeviceExact(qemuMonitorPtr mon, + int bus, + int dev) +{ + DEBUG("mon=%p, fd=%d bus=%d dev=%d", mon, mon->fd, bus, dev); + + return qemuMonitorTextAddUSBDeviceExact(mon, bus, dev); +} + +int qemuMonitorAddUSBDeviceMatch(qemuMonitorPtr mon, + int vendor, + int product) +{ + DEBUG("mon=%p, fd=%d vendor=%d product=%d", + mon, mon->fd, vendor, product); + + return qemuMonitorTextAddUSBDeviceMatch(mon, vendor, product); +} + + +int qemuMonitorAddPCIHostDevice(qemuMonitorPtr mon, + unsigned hostDomain, + unsigned hostBus, + unsigned hostSlot, + unsigned hostFunction, + unsigned *guestDomain, + unsigned *guestBus, + unsigned *guestSlot) +{ + DEBUG("mon=%p, fd=%d domain=%d bus=%d slot=%d function=%d", + mon, mon->fd, + hostDomain, hostBus, hostSlot, hostFunction); + + return qemuMonitorTextAddPCIHostDevice(mon, hostDomain, + hostBus, hostSlot, + hostFunction, + guestDomain, + guestBus, + guestSlot); +} + + +int qemuMonitorAddPCIDisk(qemuMonitorPtr mon, + const char *path, + const char *bus, + unsigned *guestDomain, + unsigned *guestBus, + unsigned *guestSlot) +{ + DEBUG("mon=%p, fd=%d path=%s bus=%s", + mon, mon->fd, path, bus); + + return qemuMonitorTextAddPCIDisk(mon, path, bus, + guestDomain, guestBus, guestSlot); +} + + +int qemuMonitorAddPCINetwork(qemuMonitorPtr mon, + const char *nicstr, + unsigned *guestDomain, + unsigned *guestBus, + unsigned *guestSlot) +{ + DEBUG("mon=%p, fd=%d nicstr=%s", mon, mon->fd, nicstr); + + return qemuMonitorTextAddPCINetwork(mon, nicstr, guestDomain, + guestBus, guestSlot); +} + + +int qemuMonitorRemovePCIDevice(qemuMonitorPtr mon, + unsigned guestDomain, + unsigned guestBus, + unsigned guestSlot) +{ + DEBUG("mon=%p, fd=%d domain=%d bus=%d slot=%d", + mon, mon->fd, guestDomain, guestBus, guestSlot); + + return qemuMonitorTextRemovePCIDevice(mon, guestDomain, + guestBus, guestSlot); +} + + +int qemuMonitorSendFileHandle(qemuMonitorPtr mon, + const char *fdname, + int fd) +{ + DEBUG("mon=%p, fd=%d fdname=%s fd=%d", + mon, mon->fd, fdname, fd); + + return qemuMonitorTextSendFileHandle(mon, fdname, fd); +} + + +int qemuMonitorCloseFileHandle(qemuMonitorPtr mon, + const char *fdname) +{ + DEBUG("mon=%p, fd=%d fdname=%s", + mon, mon->fd, fdname); + + return qemuMonitorTextCloseFileHandle(mon, fdname); +} + + +int qemuMonitorAddHostNetwork(qemuMonitorPtr mon, + const char *netstr) +{ + DEBUG("mon=%p, fd=%d netstr=%s", + mon, mon->fd, netstr); + + return qemuMonitorTextAddHostNetwork(mon, netstr); +} + + +int qemuMonitorRemoveHostNetwork(qemuMonitorPtr mon, + int vlan, + const char *netname) +{ + DEBUG("mon=%p, fd=%d netname=%s", + mon, mon->fd, netname); + + return qemuMonitorTextRemoveHostNetwork(mon, vlan, netname); +} diff --git a/src/qemu/qemu_monitor.h b/src/qemu/qemu_monitor.h index 6fb99a9..5f06155 100644 --- a/src/qemu/qemu_monitor.h +++ b/src/qemu/qemu_monitor.h @@ -78,4 +78,152 @@ int qemuMonitorGetDiskSecret(qemuMonitorPtr mon, char **secret, size_t *secretLen); + +int qemuMonitorStartCPUs(qemuMonitorPtr mon, + virConnectPtr conn); +int qemuMonitorStopCPUs(qemuMonitorPtr mon); + +int qemuMonitorSystemPowerdown(qemuMonitorPtr mon); + +int qemuMonitorGetCPUInfo(qemuMonitorPtr mon, + int **pids); +int qemuMonitorGetBalloonInfo(qemuMonitorPtr mon, + unsigned long *currmem); +int qemuMonitorGetBlockStatsInfo(qemuMonitorPtr mon, + const char *devname, + long long *rd_req, + long long *rd_bytes, + long long *wr_req, + long long *wr_bytes, + long long *errs); + + +int qemuMonitorSetVNCPassword(qemuMonitorPtr mon, + const char *password); +int qemuMonitorSetBalloon(qemuMonitorPtr mon, + unsigned long newmem); + +/* XXX should we pass the virDomainDiskDefPtr instead + * and hide devname details inside monitor. Reconsider + * this when doing the QMP implementation + */ +int qemuMonitorEjectMedia(qemuMonitorPtr mon, + const char *devname); +int qemuMonitorChangeMedia(qemuMonitorPtr mon, + const char *devname, + const char *newmedia); + + +int qemuMonitorSaveVirtualMemory(qemuMonitorPtr mon, + unsigned long long offset, + size_t length, + const char *path); +int qemuMonitorSavePhysicalMemory(qemuMonitorPtr mon, + unsigned long long offset, + size_t length, + const char *path); + +int qemuMonitorSetMigrationSpeed(qemuMonitorPtr mon, + unsigned long bandwidth); + +enum { + QEMU_MONITOR_MIGRATION_STATUS_INACTIVE, + QEMU_MONITOR_MIGRATION_STATUS_ACTIVE, + QEMU_MONITOR_MIGRATION_STATUS_COMPLETED, + QEMU_MONITOR_MIGRATION_STATUS_ERROR, + QEMU_MONITOR_MIGRATION_STATUS_CANCELLED, + + QEMU_MONITOR_MIGRATION_STATUS_LAST +}; + +int qemuMonitorGetMigrationStatus(qemuMonitorPtr mon, + int *status, + unsigned long long *transferred, + unsigned long long *remaining, + unsigned long long *total); + +int qemuMonitorMigrateToHost(qemuMonitorPtr mon, + int background, + const char *hostname, + int port); + +int qemuMonitorMigrateToCommand(qemuMonitorPtr mon, + int background, + const char * const *argv, + const char *target); + +int qemuMonitorMigrateToUnix(qemuMonitorPtr mon, + int background, + const char *unixfile); + +int qemuMonitorMigrateCancel(qemuMonitorPtr mon); + + +/* XXX disk driver type eg, qcow/etc. + * XXX cache mode + */ +int qemuMonitorAddUSBDisk(qemuMonitorPtr mon, + const char *path); + +int qemuMonitorAddUSBDeviceExact(qemuMonitorPtr mon, + int bus, + int dev); +int qemuMonitorAddUSBDeviceMatch(qemuMonitorPtr mon, + int vendor, + int product); + + +int qemuMonitorAddPCIHostDevice(qemuMonitorPtr mon, + unsigned hostDomain, + unsigned hostBus, + unsigned hostSlot, + unsigned hostFunction, + unsigned *guestDomain, + unsigned *guestBus, + unsigned *guestSlot); + +/* XXX disk driver type eg, qcow/etc. + * XXX cache mode + */ +int qemuMonitorAddPCIDisk(qemuMonitorPtr mon, + const char *path, + const char *bus, + unsigned *guestDomain, + unsigned *guestBus, + unsigned *guestSlot); + +/* XXX do we really want to hardcode 'nicstr' as the + * sendable item here + */ +int qemuMonitorAddPCINetwork(qemuMonitorPtr mon, + const char *nicstr, + unsigned *guestDomain, + unsigned *guestBus, + unsigned *guestSlot); + +int qemuMonitorRemovePCIDevice(qemuMonitorPtr mon, + unsigned guestDomain, + unsigned guestBus, + unsigned guestSlot); + + +int qemuMonitorSendFileHandle(qemuMonitorPtr mon, + const char *fdname, + int fd); + +int qemuMonitorCloseFileHandle(qemuMonitorPtr mon, + const char *fdname); + + +/* XXX do we really want to hardcode 'netstr' as the + * sendable item here + */ +int qemuMonitorAddHostNetwork(qemuMonitorPtr mon, + const char *netstr); + +int qemuMonitorRemoveHostNetwork(qemuMonitorPtr mon, + int vlan, + const char *netname); + + #endif /* QEMU_MONITOR_H */ diff --git a/src/qemu/qemu_monitor_text.c b/src/qemu/qemu_monitor_text.c index a0146ae..7ad7f09 100644 --- a/src/qemu/qemu_monitor_text.c +++ b/src/qemu/qemu_monitor_text.c @@ -45,7 +45,7 @@ #define QEMU_PASSWD_PROMPT "Password: " /* Return -1 for error, 0 for success */ -typedef int qemuMonitorExtraPromptHandler(const virDomainObjPtr vm, +typedef int qemuMonitorExtraPromptHandler(qemuMonitorPtr mon, const char *buf, const char *prompt, void *data); @@ -141,8 +141,7 @@ static char *qemuMonitorEscapeShell(const char *in) * when we reconnect to the monitor after restarts. */ static void -qemuMonitorDiscardPendingData(virDomainObjPtr vm) { - qemuDomainObjPrivatePtr priv = vm->privateData; +qemuMonitorDiscardPendingData(qemuMonitorPtr mon) { char buf[1024]; int ret = 0; @@ -150,17 +149,16 @@ qemuMonitorDiscardPendingData(virDomainObjPtr vm) { * get -1 or 0. Don't bother with detecting * errors, since we'll deal with that better later */ do { - ret = qemuMonitorRead(priv->mon, buf, sizeof (buf)-1); + ret = qemuMonitorRead(mon, buf, sizeof (buf)-1); } while (ret > 0); } static int -qemuMonitorSend(const virDomainObjPtr vm, +qemuMonitorSend(qemuMonitorPtr mon, const char *cmd, int scm_fd) { - qemuDomainObjPrivatePtr priv = vm->privateData; char *full; size_t len; int ret = -1; @@ -171,35 +169,29 @@ qemuMonitorSend(const virDomainObjPtr vm, len = strlen(full); if (scm_fd == -1) - ret = qemuMonitorWrite(priv->mon, full, len); + ret = qemuMonitorWrite(mon, full, len); else - ret = qemuMonitorWriteWithFD(priv->mon, full, len, scm_fd); + ret = qemuMonitorWriteWithFD(mon, full, len, scm_fd); VIR_FREE(full); return ret; } static int -qemuMonitorCommandWithHandler(const virDomainObjPtr vm, +qemuMonitorCommandWithHandler(qemuMonitorPtr mon, const char *cmd, const char *extraPrompt, qemuMonitorExtraPromptHandler extraHandler, void *handlerData, int scm_fd, char **reply) { - qemuDomainObjPrivatePtr priv = vm->privateData; int size = 0; char *buf = NULL; - /* Should never happen, but just in case, protect - * against null monitor (ocurrs when VM is inactive) */ - if (!priv->mon) - return -1; - - qemuMonitorDiscardPendingData(vm); + qemuMonitorDiscardPendingData(mon); VIR_DEBUG("cmd='%s' extraPrompt='%s'", cmd, NULLSTR(extraPrompt)); - if (qemuMonitorSend(vm, cmd, scm_fd) < 0) + if (qemuMonitorSend(mon, cmd, scm_fd) < 0) return -1; *reply = NULL; @@ -208,7 +200,7 @@ qemuMonitorCommandWithHandler(const virDomainObjPtr vm, /* Read all the data QEMU has sent thus far */ for (;;) { char data[1024]; - int got = qemuMonitorRead(priv->mon, data, sizeof(data)); + int got = qemuMonitorRead(mon, data, sizeof(data)); if (got == 0) goto error; @@ -237,7 +229,7 @@ qemuMonitorCommandWithHandler(const virDomainObjPtr vm, char *promptEnd; DEBUG("prompt='%s' handler=%p", extraPrompt, extraHandler); - if (extraHandler(vm, buf, foundPrompt, handlerData) < 0) + if (extraHandler(mon, buf, foundPrompt, handlerData) < 0) return -1; /* Discard output so far, necessary to detect whether extraPrompt appears again. We don't need the output between @@ -268,7 +260,7 @@ qemuMonitorCommandWithHandler(const virDomainObjPtr vm, } /* Need to wait for more data */ - if (qemuMonitorWaitForInput(priv->mon) < 0) + if (qemuMonitorWaitForInput(mon) < 0) goto error; } *reply = buf; @@ -287,7 +279,7 @@ struct extraHandlerData }; static int -qemuMonitorCommandSimpleExtraHandler(const virDomainObjPtr vm, +qemuMonitorCommandSimpleExtraHandler(qemuMonitorPtr mon, const char *buf ATTRIBUTE_UNUSED, const char *prompt ATTRIBUTE_UNUSED, void *data_) @@ -296,14 +288,14 @@ qemuMonitorCommandSimpleExtraHandler(const virDomainObjPtr vm, if (!data->first) return 0; - if (qemuMonitorSend(vm, data->reply, -1) < 0) + if (qemuMonitorSend(mon, data->reply, -1) < 0) return -1; data->first = false; return 0; } static int -qemuMonitorCommandExtra(const virDomainObjPtr vm, +qemuMonitorCommandExtra(qemuMonitorPtr mon, const char *cmd, const char *extra, const char *extraPrompt, @@ -313,35 +305,34 @@ qemuMonitorCommandExtra(const virDomainObjPtr vm, data.reply = extra; data.first = true; - return qemuMonitorCommandWithHandler(vm, cmd, extraPrompt, + return qemuMonitorCommandWithHandler(mon, cmd, extraPrompt, qemuMonitorCommandSimpleExtraHandler, &data, scm_fd, reply); } static int -qemuMonitorCommandWithFd(const virDomainObjPtr vm, +qemuMonitorCommandWithFd(qemuMonitorPtr mon, const char *cmd, int scm_fd, char **reply) { - return qemuMonitorCommandExtra(vm, cmd, NULL, NULL, scm_fd, reply); + return qemuMonitorCommandExtra(mon, cmd, NULL, NULL, scm_fd, reply); } static int -qemuMonitorCommand(const virDomainObjPtr vm, +qemuMonitorCommand(qemuMonitorPtr mon, const char *cmd, char **reply) { - return qemuMonitorCommandWithFd(vm, cmd, -1, reply); + return qemuMonitorCommandWithFd(mon, cmd, -1, reply); } static int -qemuMonitorSendVolumePassphrase(const virDomainObjPtr vm, +qemuMonitorSendVolumePassphrase(qemuMonitorPtr mon, const char *buf, const char *prompt, void *data) { virConnectPtr conn = data; - qemuDomainObjPrivatePtr priv = vm->privateData; char *passphrase = NULL, *path; const char *prompt_path; size_t path_len, passphrase_len = 0; @@ -362,13 +353,13 @@ qemuMonitorSendVolumePassphrase(const virDomainObjPtr vm, memcpy(path, prompt_path, path_len); path[path_len] = '\0'; - res = qemuMonitorGetDiskSecret(priv->mon, conn, path, + res = qemuMonitorGetDiskSecret(mon, conn, path, &passphrase, &passphrase_len); VIR_FREE(path); if (res < 0) return -1; - res = qemuMonitorSend(vm, passphrase, -1); + res = qemuMonitorSend(mon, passphrase, -1); memset(passphrase, 0, passphrase_len); VIR_FREE(passphrase); @@ -377,13 +368,11 @@ qemuMonitorSendVolumePassphrase(const virDomainObjPtr vm, } int -qemuMonitorStartCPUs(virConnectPtr conn, - const virDomainObjPtr vm) { +qemuMonitorTextStartCPUs(qemuMonitorPtr mon, + virConnectPtr conn) { char *reply; - DEBUG("vm=%p, pid=%d, id=%d, name=%s", vm, vm->pid, vm->def->id, vm->def->name); - - if (qemuMonitorCommandWithHandler(vm, "cont", ") is encrypted.", + if (qemuMonitorCommandWithHandler(mon, "cont", ") is encrypted.", qemuMonitorSendVolumePassphrase, conn, -1, &reply) < 0) return -1; @@ -394,12 +383,10 @@ qemuMonitorStartCPUs(virConnectPtr conn, int -qemuMonitorStopCPUs(const virDomainObjPtr vm) { +qemuMonitorTextStopCPUs(qemuMonitorPtr mon) { char *info; - DEBUG("vm=%p, pid=%d, id=%d, name=%s", vm, vm->pid, vm->def->id, vm->def->name); - - if (qemuMonitorCommand(vm, "stop", &info) < 0) { + if (qemuMonitorCommand(mon, "stop", &info) < 0) { qemudReportError(NULL, NULL, NULL, VIR_ERR_OPERATION_FAILED, "%s", _("cannot stop CPU execution")); return -1; @@ -409,12 +396,10 @@ qemuMonitorStopCPUs(const virDomainObjPtr vm) { } -int qemuMonitorSystemPowerdown(const virDomainObjPtr vm) { +int qemuMonitorTextSystemPowerdown(qemuMonitorPtr mon) { char *info; - DEBUG("vm=%p, pid=%d, id=%d, name=%s", vm, vm->pid, vm->def->id, vm->def->name); - - if (qemuMonitorCommand(vm, "system_powerdown", &info) < 0) { + if (qemuMonitorCommand(mon, "system_powerdown", &info) < 0) { qemudReportError(NULL, NULL, NULL, VIR_ERR_OPERATION_FAILED, "%s", _("system shutdown operation failed")); return -1; @@ -424,8 +409,8 @@ int qemuMonitorSystemPowerdown(const virDomainObjPtr vm) { } -int qemuMonitorGetCPUInfo(const virDomainObjPtr vm, - int **pids) +int qemuMonitorTextGetCPUInfo(qemuMonitorPtr mon, + int **pids) { char *qemucpus = NULL; char *line; @@ -433,9 +418,7 @@ int qemuMonitorGetCPUInfo(const virDomainObjPtr vm, pid_t *cpupids = NULL; size_t ncpupids = 0; - DEBUG("vm=%p, pid=%d, id=%d, name=%s", vm, vm->pid, vm->def->id, vm->def->name); - - if (qemuMonitorCommand(vm, "info cpus", &qemucpus) < 0) { + if (qemuMonitorCommand(mon, "info cpus", &qemucpus) < 0) { qemudReportError(NULL, NULL, NULL, VIR_ERR_INTERNAL_ERROR, "%s", _("cannot run monitor command to fetch CPU thread info")); return -1; @@ -517,16 +500,14 @@ error: * Returns: 0 if balloon not supported, +1 if balloon query worked * or -1 on failure */ -int qemuMonitorGetBalloonInfo(const virDomainObjPtr vm, - unsigned long *currmem) +int qemuMonitorTextGetBalloonInfo(qemuMonitorPtr mon, + unsigned long *currmem) { char *reply = NULL; int ret = -1; char *offset; - DEBUG("vm=%p, pid=%d, id=%d, name=%s", vm, vm->pid, vm->def->id, vm->def->name); - - if (qemuMonitorCommand(vm, "info balloon", &reply) < 0) { + if (qemuMonitorCommand(mon, "info balloon", &reply) < 0) { qemudReportError(NULL, NULL, NULL, VIR_ERR_OPERATION_FAILED, "%s", _("could not query memory balloon allocation")); return -1; @@ -556,13 +537,13 @@ cleanup: } -int qemuMonitorGetBlockStatsInfo(const virDomainObjPtr vm, - const char *devname, - long long *rd_req, - long long *rd_bytes, - long long *wr_req, - long long *wr_bytes, - long long *errs) +int qemuMonitorTextGetBlockStatsInfo(qemuMonitorPtr mon, + const char *devname, + long long *rd_req, + long long *rd_bytes, + long long *wr_req, + long long *wr_bytes, + long long *errs) { char *info = NULL; int ret = -1; @@ -570,10 +551,7 @@ int qemuMonitorGetBlockStatsInfo(const virDomainObjPtr vm, const char *p, *eol; int devnamelen = strlen(devname); - DEBUG("vm=%p, pid=%d, id=%d, name=%s dev=%s", - vm, vm->pid, vm->def->id, vm->def->name, devname); - - if (qemuMonitorCommand (vm, "info blockstats", &info) < 0) { + if (qemuMonitorCommand (mon, "info blockstats", &info) < 0) { qemudReportError (NULL, NULL, NULL, VIR_ERR_OPERATION_FAILED, "%s", _("'info blockstats' command failed")); goto cleanup; @@ -618,25 +596,21 @@ int qemuMonitorGetBlockStatsInfo(const virDomainObjPtr vm, if (STRPREFIX (p, "rd_bytes=")) { p += 9; if (virStrToLong_ll (p, &dummy, 10, rd_bytes) == -1) - DEBUG ("%s: error reading rd_bytes: %s", - vm->def->name, p); + DEBUG ("error reading rd_bytes: %s", p); } else if (STRPREFIX (p, "wr_bytes=")) { p += 9; if (virStrToLong_ll (p, &dummy, 10, wr_bytes) == -1) - DEBUG ("%s: error reading wr_bytes: %s", - vm->def->name, p); + DEBUG ("error reading wr_bytes: %s", p); } else if (STRPREFIX (p, "rd_operations=")) { p += 14; if (virStrToLong_ll (p, &dummy, 10, rd_req) == -1) - DEBUG ("%s: error reading rd_req: %s", - vm->def->name, p); + DEBUG ("error reading rd_req: %s", p); } else if (STRPREFIX (p, "wr_operations=")) { p += 14; if (virStrToLong_ll (p, &dummy, 10, wr_req) == -1) - DEBUG ("%s: error reading wr_req: %s", - vm->def->name, p); + DEBUG ("error reading wr_req: %s", p); } else - DEBUG ("%s: unknown block stat near %s", vm->def->name, p); + DEBUG ("unknown block stat near %s", p); /* Skip to next label. */ p = strchr (p, ' '); @@ -663,14 +637,12 @@ int qemuMonitorGetBlockStatsInfo(const virDomainObjPtr vm, } -int qemuMonitorSetVNCPassword(const virDomainObjPtr vm, - const char *password) +int qemuMonitorTextSetVNCPassword(qemuMonitorPtr mon, + const char *password) { char *info = NULL; - DEBUG("vm=%p, pid=%d, id=%d, name=%s", vm, vm->pid, vm->def->id, vm->def->name); - - if (qemuMonitorCommandExtra(vm, "change vnc password", + if (qemuMonitorCommandExtra(mon, "change vnc password", password, QEMU_PASSWD_PROMPT, -1, &info) < 0) { @@ -686,16 +658,13 @@ int qemuMonitorSetVNCPassword(const virDomainObjPtr vm, * Returns: 0 if balloon not supported, +1 if balloon adjust worked * or -1 on failure */ -int qemuMonitorSetBalloon(const virDomainObjPtr vm, - unsigned long newmem) +int qemuMonitorTextSetBalloon(qemuMonitorPtr mon, + unsigned long newmem) { char *cmd; char *reply = NULL; int ret = -1; - DEBUG("vm=%p, pid=%d, id=%d, name=%s newmem=%lu", - vm, vm->pid, vm->def->id, vm->def->name, newmem); - /* * 'newmem' is in KB, QEMU monitor works in MB, and we all wish * we just worked in bytes with unsigned long long everywhere. @@ -705,7 +674,7 @@ int qemuMonitorSetBalloon(const virDomainObjPtr vm, return -1; } - if (qemuMonitorCommand(vm, cmd, &reply) < 0) { + if (qemuMonitorCommand(mon, cmd, &reply) < 0) { qemudReportError(NULL, NULL, NULL, VIR_ERR_OPERATION_FAILED, "%s", _("could not balloon memory allocation")); VIR_FREE(cmd); @@ -726,22 +695,19 @@ int qemuMonitorSetBalloon(const virDomainObjPtr vm, return ret; } -int qemuMonitorEjectMedia(const virDomainObjPtr vm, - const char *devname) +int qemuMonitorTextEjectMedia(qemuMonitorPtr mon, + const char *devname) { char *cmd = NULL; char *reply = NULL; int ret = -1; - DEBUG("vm=%p, pid=%d, id=%d, name=%s devname=%s", - vm, vm->pid, vm->def->id, vm->def->name, devname); - if (virAsprintf(&cmd, "eject %s", devname) < 0) { virReportOOMError(NULL); goto cleanup; } - if (qemuMonitorCommand(vm, cmd, &reply) < 0) { + if (qemuMonitorCommand(mon, cmd, &reply) < 0) { qemudReportError(NULL, NULL, NULL, VIR_ERR_OPERATION_FAILED, _("could not eject media on %s"), devname); goto cleanup; @@ -765,18 +731,15 @@ cleanup: } -int qemuMonitorChangeMedia(const virDomainObjPtr vm, - const char *devname, - const char *newmedia) +int qemuMonitorTextChangeMedia(qemuMonitorPtr mon, + const char *devname, + const char *newmedia) { char *cmd = NULL; char *reply = NULL; char *safepath = NULL; int ret = -1; - DEBUG("vm=%p, pid=%d, id=%d, name=%s devname=%s newmedia=%s", - vm, vm->pid, vm->def->id, vm->def->name, devname, newmedia); - if (!(safepath = qemuMonitorEscapeArg(newmedia))) { virReportOOMError(NULL); goto cleanup; @@ -787,7 +750,7 @@ int qemuMonitorChangeMedia(const virDomainObjPtr vm, goto cleanup; } - if (qemuMonitorCommand(vm, cmd, &reply) < 0) { + if (qemuMonitorCommand(mon, cmd, &reply) < 0) { qemudReportError(NULL, NULL, NULL, VIR_ERR_OPERATION_FAILED, _("could not eject media on %s"), devname); goto cleanup; @@ -811,11 +774,11 @@ cleanup: return ret; } -static int qemuMonitorSaveMemory(const virDomainObjPtr vm, - const char *cmdtype, - unsigned long long offset, - size_t length, - const char *path) +static int qemuMonitorTextSaveMemory(qemuMonitorPtr mon, + const char *cmdtype, + unsigned long long offset, + size_t length, + const char *path) { char *cmd = NULL; char *reply = NULL; @@ -832,7 +795,7 @@ static int qemuMonitorSaveMemory(const virDomainObjPtr vm, goto cleanup; } - if (qemuMonitorCommand(vm, cmd, &reply) < 0) { + if (qemuMonitorCommand(mon, cmd, &reply) < 0) { qemudReportError(NULL, NULL, NULL, VIR_ERR_OPERATION_FAILED, _("could save memory region to '%s'"), path); goto cleanup; @@ -850,45 +813,36 @@ cleanup: } -int qemuMonitorSaveVirtualMemory(const virDomainObjPtr vm, - unsigned long long offset, - size_t length, - const char *path) +int qemuMonitorTextSaveVirtualMemory(qemuMonitorPtr mon, + unsigned long long offset, + size_t length, + const char *path) { - DEBUG("vm=%p, pid=%d, id=%d, name=%s offset=%llu length=%zu path=%s", - vm, vm->pid, vm->def->id, vm->def->name, offset, length, path); - - return qemuMonitorSaveMemory(vm, "memsave", offset, length, path); + return qemuMonitorTextSaveMemory(mon, "memsave", offset, length, path); } -int qemuMonitorSavePhysicalMemory(const virDomainObjPtr vm, - unsigned long long offset, - size_t length, - const char *path) +int qemuMonitorTextSavePhysicalMemory(qemuMonitorPtr mon, + unsigned long long offset, + size_t length, + const char *path) { - DEBUG("vm=%p, pid=%d, id=%d, name=%s offset=%llu length=%zu path=%s", - vm, vm->pid, vm->def->id, vm->def->name, offset, length, path); - - return qemuMonitorSaveMemory(vm, "pmemsave", offset, length, path); + return qemuMonitorTextSaveMemory(mon, "pmemsave", offset, length, path); } -int qemuMonitorSetMigrationSpeed(const virDomainObjPtr vm, - unsigned long bandwidth) +int qemuMonitorTextSetMigrationSpeed(qemuMonitorPtr mon, + unsigned long bandwidth) { char *cmd = NULL; char *info = NULL; int ret = -1; - DEBUG("vm=%p, pid=%d, id=%d, name=%s bandwidth=%lu", - vm, vm->pid, vm->def->id, vm->def->name, bandwidth); - if (virAsprintf(&cmd, "migrate_set_speed %lum", bandwidth) < 0) { virReportOOMError(NULL); goto cleanup; } - if (qemuMonitorCommand(vm, cmd, &info) < 0) { + if (qemuMonitorCommand(mon, cmd, &info) < 0) { qemudReportError(NULL, NULL, NULL, VIR_ERR_OPERATION_FAILED, "%s", _("could restrict migration speed")); goto cleanup; @@ -913,24 +867,22 @@ VIR_ENUM_IMPL(qemuMonitorMigrationStatus, QEMU_MONITOR_MIGRATION_STATUS_LAST, "inactive", "active", "completed", "failed", "cancelled") -int qemuMonitorGetMigrationStatus(const virDomainObjPtr vm, - int *status, - unsigned long long *transferred, - unsigned long long *remaining, - unsigned long long *total) { +int qemuMonitorTextGetMigrationStatus(qemuMonitorPtr mon, + int *status, + unsigned long long *transferred, + unsigned long long *remaining, + unsigned long long *total) { char *reply; char *tmp; char *end; int ret = -1; - DEBUG("vm=%p, pid=%d, id=%d, name=%s", vm, vm->pid, vm->def->id, vm->def->name); - *status = QEMU_MONITOR_MIGRATION_STATUS_INACTIVE; *transferred = 0; *remaining = 0; *total = 0; - if (qemuMonitorCommand(vm, "info migrate", &reply) < 0) { + if (qemuMonitorCommand(mon, "info migrate", &reply) < 0) { qemudReportError(NULL, NULL, NULL, VIR_ERR_OPERATION_FAILED, "%s", _("cannot query migration status")); return -1; @@ -995,9 +947,9 @@ cleanup: } -static int qemuMonitorMigrate(const virDomainObjPtr vm, - int background, - const char *dest) +static int qemuMonitorTextMigrate(qemuMonitorPtr mon, + int background, + const char *dest) { char *cmd = NULL; char *info = NULL; @@ -1020,7 +972,7 @@ static int qemuMonitorMigrate(const virDomainObjPtr vm, goto cleanup; } - if (qemuMonitorCommand(vm, cmd, &info) < 0) { + if (qemuMonitorCommand(mon, cmd, &info) < 0) { qemudReportError(NULL, NULL, NULL, VIR_ERR_INTERNAL_ERROR, _("unable to start migration to %s"), dest); goto cleanup; @@ -1050,23 +1002,20 @@ cleanup: return ret; } -int qemuMonitorMigrateToHost(const virDomainObjPtr vm, - int background, - const char *hostname, - int port) +int qemuMonitorTextMigrateToHost(qemuMonitorPtr mon, + int background, + const char *hostname, + int port) { char *uri = NULL; int ret; - DEBUG("vm=%p, pid=%d, id=%d, name=%s hostname=%s port=%d", - vm, vm->pid, vm->def->id, vm->def->name, hostname, port); - if (virAsprintf(&uri, "tcp:%s:%d", hostname, port) < 0) { virReportOOMError(NULL); return -1; } - ret = qemuMonitorMigrate(vm, background, uri); + ret = qemuMonitorTextMigrate(mon, background, uri); VIR_FREE(uri); @@ -1074,19 +1023,16 @@ int qemuMonitorMigrateToHost(const virDomainObjPtr vm, } -int qemuMonitorMigrateToCommand(const virDomainObjPtr vm, - int background, - const char * const *argv, - const char *target) +int qemuMonitorTextMigrateToCommand(qemuMonitorPtr mon, + int background, + const char * const *argv, + const char *target) { char *argstr; char *dest = NULL; int ret = -1; char *safe_target = NULL; - DEBUG("vm=%p, pid=%d, id=%d, name=%s argv=%p target=%s", - vm, vm->pid, vm->def->id, vm->def->name, argv, target); - argstr = virArgvToString(argv); if (!argstr) { virReportOOMError(NULL); @@ -1105,7 +1051,7 @@ int qemuMonitorMigrateToCommand(const virDomainObjPtr vm, goto cleanup; } - ret = qemuMonitorMigrate(vm, background, dest); + ret = qemuMonitorTextMigrate(mon, background, dest); cleanup: VIR_FREE(safe_target); @@ -1114,7 +1060,7 @@ cleanup: return ret; } -int qemuMonitorMigrateToUnix(const virDomainObjPtr vm, +int qemuMonitorTextMigrateToUnix(qemuMonitorPtr mon, int background, const char *unixfile) { @@ -1126,18 +1072,18 @@ int qemuMonitorMigrateToUnix(const virDomainObjPtr vm, return -1; } - ret = qemuMonitorMigrate(vm, background, dest); + ret = qemuMonitorTextMigrate(mon, background, dest); VIR_FREE(dest); return ret; } -int qemuMonitorMigrateCancel(const virDomainObjPtr vm) +int qemuMonitorTextMigrateCancel(qemuMonitorPtr mon) { char *info = NULL; - if (qemuMonitorCommand(vm, "migrate cancel", &info) < 0) { + if (qemuMonitorCommand(mon, "migrate cancel", &info) < 0) { qemudReportError(NULL, NULL, NULL, VIR_ERR_INTERNAL_ERROR, "%s", _("cannot run monitor command to cancel migration")); return -1; @@ -1147,17 +1093,14 @@ int qemuMonitorMigrateCancel(const virDomainObjPtr vm) return 0; } -int qemuMonitorAddUSBDisk(const virDomainObjPtr vm, - const char *path) +int qemuMonitorTextAddUSBDisk(qemuMonitorPtr mon, + const char *path) { char *cmd = NULL; char *safepath; int ret = -1; char *info = NULL; - DEBUG("vm=%p, pid=%d, id=%d, name=%s path=%s", - vm, vm->pid, vm->def->id, vm->def->name, path); - safepath = qemuMonitorEscapeArg(path); if (!safepath) { virReportOOMError(NULL); @@ -1169,7 +1112,7 @@ int qemuMonitorAddUSBDisk(const virDomainObjPtr vm, goto cleanup; } - if (qemuMonitorCommand(vm, cmd, &info) < 0) { + if (qemuMonitorCommand(mon, cmd, &info) < 0) { qemudReportError(NULL, NULL, NULL, VIR_ERR_INTERNAL_ERROR, "%s", _("cannot run monitor command to add usb disk")); goto cleanup; @@ -1192,8 +1135,8 @@ cleanup: } -static int qemuMonitorAddUSBDevice(const virDomainObjPtr vm, - const char *addr) +static int qemuMonitorTextAddUSBDevice(qemuMonitorPtr mon, + const char *addr) { char *cmd; char *reply = NULL; @@ -1204,7 +1147,7 @@ static int qemuMonitorAddUSBDevice(const virDomainObjPtr vm, return -1; } - if (qemuMonitorCommand(vm, cmd, &reply) < 0) { + if (qemuMonitorCommand(mon, cmd, &reply) < 0) { qemudReportError(NULL, NULL, NULL, VIR_ERR_OPERATION_FAILED, "%s", _("cannot attach usb device")); goto cleanup; @@ -1227,43 +1170,37 @@ cleanup: } -int qemuMonitorAddUSBDeviceExact(const virDomainObjPtr vm, - int bus, - int dev) +int qemuMonitorTextAddUSBDeviceExact(qemuMonitorPtr mon, + int bus, + int dev) { int ret; char *addr; - DEBUG("vm=%p, pid=%d, id=%d, name=%s bus=%d dev=%d", - vm, vm->pid, vm->def->id, vm->def->name, bus, dev); - if (virAsprintf(&addr, "host:%.3d.%.3d", bus, dev) < 0) { virReportOOMError(NULL); return -1; } - ret = qemuMonitorAddUSBDevice(vm, addr); + ret = qemuMonitorTextAddUSBDevice(mon, addr); VIR_FREE(addr); return ret; } -int qemuMonitorAddUSBDeviceMatch(const virDomainObjPtr vm, - int vendor, - int product) +int qemuMonitorTextAddUSBDeviceMatch(qemuMonitorPtr mon, + int vendor, + int product) { int ret; char *addr; - DEBUG("vm=%p, pid=%d, id=%d, name=%s vendor=%d product=%d", - vm, vm->pid, vm->def->id, vm->def->name, vendor, product); - if (virAsprintf(&addr, "host:%.4x:%.4x", vendor, product) < 0) { virReportOOMError(NULL); return -1; } - ret = qemuMonitorAddUSBDevice(vm, addr); + ret = qemuMonitorTextAddUSBDevice(mon, addr); VIR_FREE(addr); return ret; @@ -1271,16 +1208,14 @@ int qemuMonitorAddUSBDeviceMatch(const virDomainObjPtr vm, static int -qemuMonitorParsePciAddReply(virDomainObjPtr vm, - const char *reply, - unsigned *domain, - unsigned *bus, - unsigned *slot) +qemuMonitorTextParsePciAddReply(qemuMonitorPtr mon ATTRIBUTE_UNUSED, + const char *reply, + unsigned *domain, + unsigned *bus, + unsigned *slot) { char *s, *e; - DEBUG("%s: pci_add reply: %s", vm->def->name, reply); - /* If the command succeeds qemu prints: * OK bus 0, slot XXX... * or @@ -1338,23 +1273,19 @@ qemuMonitorParsePciAddReply(virDomainObjPtr vm, } -int qemuMonitorAddPCIHostDevice(const virDomainObjPtr vm, - unsigned hostDomain ATTRIBUTE_UNUSED, - unsigned hostBus, - unsigned hostSlot, - unsigned hostFunction, - unsigned *guestDomain, - unsigned *guestBus, - unsigned *guestSlot) +int qemuMonitorTextAddPCIHostDevice(qemuMonitorPtr mon, + unsigned hostDomain ATTRIBUTE_UNUSED, + unsigned hostBus, + unsigned hostSlot, + unsigned hostFunction, + unsigned *guestDomain, + unsigned *guestBus, + unsigned *guestSlot) { char *cmd; char *reply = NULL; int ret = -1; - DEBUG("vm=%p, pid=%d, id=%d, name=%s domain=%d bus=%d slot=%d function=%d", - vm, vm->pid, vm->def->id, vm->def->name, - hostDomain, hostBus, hostSlot, hostFunction); - *guestDomain = *guestBus = *guestSlot = 0; /* XXX hostDomain */ @@ -1364,7 +1295,7 @@ int qemuMonitorAddPCIHostDevice(const virDomainObjPtr vm, goto cleanup; } - if (qemuMonitorCommand(vm, cmd, &reply) < 0) { + if (qemuMonitorCommand(mon, cmd, &reply) < 0) { qemudReportError(NULL, NULL, NULL, VIR_ERR_OPERATION_FAILED, "%s", _("cannot attach host pci device")); goto cleanup; @@ -1376,10 +1307,10 @@ int qemuMonitorAddPCIHostDevice(const virDomainObjPtr vm, goto cleanup; } - if (qemuMonitorParsePciAddReply(vm, reply, - guestDomain, - guestBus, - guestSlot) < 0) { + if (qemuMonitorTextParsePciAddReply(mon, reply, + guestDomain, + guestBus, + guestSlot) < 0) { qemudReportError(NULL, NULL, NULL, VIR_ERR_OPERATION_FAILED, _("parsing pci_add reply failed: %s"), reply); goto cleanup; @@ -1394,21 +1325,18 @@ cleanup: } -int qemuMonitorAddPCIDisk(const virDomainObjPtr vm, - const char *path, - const char *bus, - unsigned *guestDomain, - unsigned *guestBus, - unsigned *guestSlot) { +int qemuMonitorTextAddPCIDisk(qemuMonitorPtr mon, + const char *path, + const char *bus, + unsigned *guestDomain, + unsigned *guestBus, + unsigned *guestSlot) { char *cmd = NULL; char *reply = NULL; char *safe_path = NULL; int tryOldSyntax = 0; int ret = -1; - DEBUG("vm=%p, pid=%d, id=%d, name=%s path=%s bus=%s", - vm, vm->pid, vm->def->id, vm->def->name, path, bus); - safe_path = qemuMonitorEscapeArg(path); if (!safe_path) { virReportOOMError(NULL); @@ -1422,14 +1350,14 @@ try_command: goto cleanup; } - if (qemuMonitorCommand(vm, cmd, &reply) < 0) { + if (qemuMonitorCommand(mon, cmd, &reply) < 0) { qemudReportError(NULL, NULL, NULL, VIR_ERR_OPERATION_FAILED, _("cannot attach %s disk %s"), bus, path); goto cleanup; } - if (qemuMonitorParsePciAddReply(vm, reply, - guestDomain, guestBus, guestSlot) < 0) { + if (qemuMonitorTextParsePciAddReply(mon, reply, + guestDomain, guestBus, guestSlot) < 0) { if (!tryOldSyntax && strstr(reply, "invalid char in expression")) { VIR_FREE(reply); VIR_FREE(cmd); @@ -1452,32 +1380,29 @@ cleanup: } -int qemuMonitorAddPCINetwork(const virDomainObjPtr vm, - const char *nicstr, - unsigned *guestDomain, - unsigned *guestBus, - unsigned *guestSlot) +int qemuMonitorTextAddPCINetwork(qemuMonitorPtr mon, + const char *nicstr, + unsigned *guestDomain, + unsigned *guestBus, + unsigned *guestSlot) { char *cmd; char *reply = NULL; int ret = -1; - DEBUG("vm=%p, pid=%d, id=%d, name=%s nicstr=%s", - vm, vm->pid, vm->def->id, vm->def->name, nicstr); - if (virAsprintf(&cmd, "pci_add pci_addr=auto nic %s", nicstr) < 0) { virReportOOMError(NULL); return -1; } - if (qemuMonitorCommand(vm, cmd, &reply) < 0) { + if (qemuMonitorCommand(mon, cmd, &reply) < 0) { qemudReportError(NULL, NULL, NULL, VIR_ERR_OPERATION_FAILED, _("failed to add NIC with '%s'"), cmd); goto cleanup; } - if (qemuMonitorParsePciAddReply(vm, reply, - guestDomain, guestBus, guestSlot) < 0) { + if (qemuMonitorTextParsePciAddReply(mon, reply, + guestDomain, guestBus, guestSlot) < 0) { qemudReportError(NULL, NULL, NULL, VIR_ERR_OPERATION_FAILED, _("parsing pci_add reply failed: %s"), reply); goto cleanup; @@ -1492,19 +1417,16 @@ cleanup: } -int qemuMonitorRemovePCIDevice(const virDomainObjPtr vm, - unsigned guestDomain, - unsigned guestBus, - unsigned guestSlot) +int qemuMonitorTextRemovePCIDevice(qemuMonitorPtr mon, + unsigned guestDomain, + unsigned guestBus, + unsigned guestSlot) { char *cmd = NULL; char *reply = NULL; int tryOldSyntax = 0; int ret = -1; - DEBUG("vm=%p, pid=%d, id=%d, name=%s domain=%d bus=%d slot=%d", - vm, vm->pid, vm->def->id, vm->def->name, guestDomain, guestBus, guestSlot); - try_command: if (tryOldSyntax) { if (virAsprintf(&cmd, "pci_del 0 %.2x", guestSlot) < 0) { @@ -1519,7 +1441,7 @@ try_command: } } - if (qemuMonitorCommand(vm, cmd, &reply) < 0) { + if (qemuMonitorCommand(mon, cmd, &reply) < 0) { qemudReportError(NULL, NULL, NULL, VIR_ERR_OPERATION_FAILED, "%s", _("failed to remove PCI device")); goto cleanup; @@ -1554,23 +1476,20 @@ cleanup: } -int qemuMonitorSendFileHandle(const virDomainObjPtr vm, - const char *fdname, - int fd) +int qemuMonitorTextSendFileHandle(qemuMonitorPtr mon, + const char *fdname, + int fd) { char *cmd; char *reply = NULL; int ret = -1; - DEBUG("vm=%p, pid=%d, id=%d, name=%s fdname=%s fd=%d", - vm, vm->pid, vm->def->id, vm->def->name, fdname, fd); - if (virAsprintf(&cmd, "getfd %s", fdname) < 0) { virReportOOMError(NULL); return -1; } - if (qemuMonitorCommandWithFd(vm, cmd, fd, &reply) < 0) { + if (qemuMonitorCommandWithFd(mon, cmd, fd, &reply) < 0) { qemudReportError(NULL, NULL, NULL, VIR_ERR_OPERATION_FAILED, _("failed to pass fd to qemu with '%s'"), cmd); goto cleanup; @@ -1594,22 +1513,19 @@ cleanup: } -int qemuMonitorCloseFileHandle(const virDomainObjPtr vm, - const char *fdname) +int qemuMonitorTextCloseFileHandle(qemuMonitorPtr mon, + const char *fdname) { char *cmd; char *reply = NULL; int ret = -1; - DEBUG("vm=%p, pid=%d, id=%d, name=%s fdname=%s", - vm, vm->pid, vm->def->id, vm->def->name, fdname); - if (virAsprintf(&cmd, "closefd %s", fdname) < 0) { virReportOOMError(NULL); return -1; } - if (qemuMonitorCommand(vm, cmd, &reply) < 0) { + if (qemuMonitorCommand(mon, cmd, &reply) < 0) { qemudReportError(NULL, NULL, NULL, VIR_ERR_OPERATION_FAILED, _("failed to close fd in qemu with '%s'"), cmd); goto cleanup; @@ -1633,22 +1549,19 @@ cleanup: } -int qemuMonitorAddHostNetwork(const virDomainObjPtr vm, - const char *netstr) +int qemuMonitorTextAddHostNetwork(qemuMonitorPtr mon, + const char *netstr) { char *cmd; char *reply = NULL; int ret = -1; - DEBUG("vm=%p, pid=%d, id=%d, name=%s netstr=%s", - vm, vm->pid, vm->def->id, vm->def->name, netstr); - if (virAsprintf(&cmd, "host_net_add %s", netstr) < 0) { virReportOOMError(NULL); return -1; } - if (qemuMonitorCommand(vm, cmd, &reply) < 0) { + if (qemuMonitorCommand(mon, cmd, &reply) < 0) { qemudReportError(NULL, NULL, NULL, VIR_ERR_OPERATION_FAILED, _("failed to close fd in qemu with '%s'"), cmd); goto cleanup; @@ -1665,23 +1578,20 @@ cleanup: } -int qemuMonitorRemoveHostNetwork(const virDomainObjPtr vm, - int vlan, - const char *netname) +int qemuMonitorTextRemoveHostNetwork(qemuMonitorPtr mon, + int vlan, + const char *netname) { char *cmd; char *reply = NULL; int ret = -1; - DEBUG("vm=%p, pid=%d, id=%d, name=%s netname=%s", - vm, vm->pid, vm->def->id, vm->def->name, netname); - if (virAsprintf(&cmd, "host_net_remove %d %s", vlan, netname) < 0) { virReportOOMError(NULL); return -1; } - if (qemuMonitorCommand(vm, cmd, &reply) < 0) { + if (qemuMonitorCommand(mon, cmd, &reply) < 0) { qemudReportError(NULL, NULL, NULL, VIR_ERR_OPERATION_FAILED, _("failed to remove host metnwork in qemu with '%s'"), cmd); goto cleanup; diff --git a/src/qemu/qemu_monitor_text.h b/src/qemu/qemu_monitor_text.h index 9175456..35eaf05 100644 --- a/src/qemu/qemu_monitor_text.h +++ b/src/qemu/qemu_monitor_text.h @@ -27,155 +27,124 @@ #include "internal.h" -#include "domain_conf.h" - - -/* Formal APIs for each required monitor command */ - -int qemuMonitorStartCPUs(virConnectPtr conn, - const virDomainObjPtr vm); -int qemuMonitorStopCPUs(const virDomainObjPtr vm); - -int qemuMonitorSystemPowerdown(const virDomainObjPtr vm); - -int qemuMonitorGetCPUInfo(const virDomainObjPtr vm, - int **pids); -int qemuMonitorGetBalloonInfo(const virDomainObjPtr vm, - unsigned long *currmem); -int qemuMonitorGetBlockStatsInfo(const virDomainObjPtr vm, - const char *devname, - long long *rd_req, - long long *rd_bytes, - long long *wr_req, - long long *wr_bytes, - long long *errs); - - -int qemuMonitorSetVNCPassword(const virDomainObjPtr vm, - const char *password); -int qemuMonitorSetBalloon(const virDomainObjPtr vm, - unsigned long newmem); - -/* XXX should we pass the virDomainDiskDefPtr instead - * and hide devname details inside monitor. Reconsider - * this when doing the QMP implementation - */ -int qemuMonitorEjectMedia(const virDomainObjPtr vm, - const char *devname); -int qemuMonitorChangeMedia(const virDomainObjPtr vm, - const char *devname, - const char *newmedia); - - -int qemuMonitorSaveVirtualMemory(const virDomainObjPtr vm, - unsigned long long offset, - size_t length, - const char *path); -int qemuMonitorSavePhysicalMemory(const virDomainObjPtr vm, - unsigned long long offset, - size_t length, - const char *path); - -int qemuMonitorSetMigrationSpeed(const virDomainObjPtr vm, - unsigned long bandwidth); - -enum { - QEMU_MONITOR_MIGRATION_STATUS_INACTIVE, - QEMU_MONITOR_MIGRATION_STATUS_ACTIVE, - QEMU_MONITOR_MIGRATION_STATUS_COMPLETED, - QEMU_MONITOR_MIGRATION_STATUS_ERROR, - QEMU_MONITOR_MIGRATION_STATUS_CANCELLED, - - QEMU_MONITOR_MIGRATION_STATUS_LAST -}; - -int qemuMonitorGetMigrationStatus(const virDomainObjPtr vm, - int *status, - unsigned long long *transferred, - unsigned long long *remaining, - unsigned long long *total); - -int qemuMonitorMigrateToHost(const virDomainObjPtr vm, - int background, - const char *hostname, - int port); - -int qemuMonitorMigrateToCommand(const virDomainObjPtr vm, - int background, - const char * const *argv, - const char *target); - -int qemuMonitorMigrateToUnix(const virDomainObjPtr vm, - int background, - const char *unixfile); - -int qemuMonitorMigrateCancel(const virDomainObjPtr vm); - - -/* XXX disk driver type eg, qcow/etc. - * XXX cache mode - */ -int qemuMonitorAddUSBDisk(const virDomainObjPtr vm, - const char *path); - -int qemuMonitorAddUSBDeviceExact(const virDomainObjPtr vm, - int bus, - int dev); -int qemuMonitorAddUSBDeviceMatch(const virDomainObjPtr vm, - int vendor, - int product); - - -int qemuMonitorAddPCIHostDevice(const virDomainObjPtr vm, - unsigned hostDomain, - unsigned hostBus, - unsigned hostSlot, - unsigned hostFunction, - unsigned *guestDomain, - unsigned *guestBus, - unsigned *guestSlot); - -/* XXX disk driver type eg, qcow/etc. - * XXX cache mode - */ -int qemuMonitorAddPCIDisk(const virDomainObjPtr vm, - const char *path, - const char *bus, - unsigned *guestDomain, - unsigned *guestBus, - unsigned *guestSlot); - -/* XXX do we really want to hardcode 'nicstr' as the - * sendable item here - */ -int qemuMonitorAddPCINetwork(const virDomainObjPtr vm, - const char *nicstr, - unsigned *guestDomain, - unsigned *guestBus, - unsigned *guestSlot); - -int qemuMonitorRemovePCIDevice(const virDomainObjPtr vm, - unsigned guestDomain, - unsigned guestBus, - unsigned guestSlot); - - -int qemuMonitorSendFileHandle(const virDomainObjPtr vm, - const char *fdname, - int fd); - -int qemuMonitorCloseFileHandle(const virDomainObjPtr vm, - const char *fdname); - - -/* XXX do we relaly want to hardcode 'netstr' as the - * sendable item here - */ -int qemuMonitorAddHostNetwork(const virDomainObjPtr vm, - const char *netstr); - -int qemuMonitorRemoveHostNetwork(const virDomainObjPtr vm, - int vlan, - const char *netname); +#include "qemu_monitor.h" + +int qemuMonitorTextStartCPUs(qemuMonitorPtr mon, + virConnectPtr conn); +int qemuMonitorTextStopCPUs(qemuMonitorPtr mon); + +int qemuMonitorTextSystemPowerdown(qemuMonitorPtr mon); + +int qemuMonitorTextGetCPUInfo(qemuMonitorPtr mon, + int **pids); +int qemuMonitorTextGetBalloonInfo(qemuMonitorPtr mon, + unsigned long *currmem); +int qemuMonitorTextGetBlockStatsInfo(qemuMonitorPtr mon, + const char *devname, + long long *rd_req, + long long *rd_bytes, + long long *wr_req, + long long *wr_bytes, + long long *errs); + + +int qemuMonitorTextSetVNCPassword(qemuMonitorPtr mon, + const char *password); +int qemuMonitorTextSetBalloon(qemuMonitorPtr mon, + unsigned long newmem); + +int qemuMonitorTextEjectMedia(qemuMonitorPtr mon, + const char *devname); +int qemuMonitorTextChangeMedia(qemuMonitorPtr mon, + const char *devname, + const char *newmedia); + + +int qemuMonitorTextSaveVirtualMemory(qemuMonitorPtr mon, + unsigned long long offset, + size_t length, + const char *path); +int qemuMonitorTextSavePhysicalMemory(qemuMonitorPtr mon, + unsigned long long offset, + size_t length, + const char *path); + +int qemuMonitorTextSetMigrationSpeed(qemuMonitorPtr mon, + unsigned long bandwidth); + +int qemuMonitorTextGetMigrationStatus(qemuMonitorPtr mon, + int *status, + unsigned long long *transferred, + unsigned long long *remaining, + unsigned long long *total); + +int qemuMonitorTextMigrateToHost(qemuMonitorPtr mon, + int background, + const char *hostname, + int port); + +int qemuMonitorTextMigrateToCommand(qemuMonitorPtr mon, + int background, + const char * const *argv, + const char *target); + +int qemuMonitorTextMigrateToUnix(qemuMonitorPtr mon, + int background, + const char *unixfile); + +int qemuMonitorTextMigrateCancel(qemuMonitorPtr mon); + +int qemuMonitorTextAddUSBDisk(qemuMonitorPtr mon, + const char *path); + +int qemuMonitorTextAddUSBDeviceExact(qemuMonitorPtr mon, + int bus, + int dev); +int qemuMonitorTextAddUSBDeviceMatch(qemuMonitorPtr mon, + int vendor, + int product); + + +int qemuMonitorTextAddPCIHostDevice(qemuMonitorPtr mon, + unsigned hostDomain, + unsigned hostBus, + unsigned hostSlot, + unsigned hostFunction, + unsigned *guestDomain, + unsigned *guestBus, + unsigned *guestSlot); + +int qemuMonitorTextAddPCIDisk(qemuMonitorPtr mon, + const char *path, + const char *bus, + unsigned *guestDomain, + unsigned *guestBus, + unsigned *guestSlot); + +int qemuMonitorTextAddPCINetwork(qemuMonitorPtr mon, + const char *nicstr, + unsigned *guestDomain, + unsigned *guestBus, + unsigned *guestSlot); + +int qemuMonitorTextRemovePCIDevice(qemuMonitorPtr mon, + unsigned guestDomain, + unsigned guestBus, + unsigned guestSlot); + + +int qemuMonitorTextSendFileHandle(qemuMonitorPtr mon, + const char *fdname, + int fd); + +int qemuMonitorTextCloseFileHandle(qemuMonitorPtr mon, + const char *fdname); + +int qemuMonitorTextAddHostNetwork(qemuMonitorPtr mon, + const char *netstr); + +int qemuMonitorTextRemoveHostNetwork(qemuMonitorPtr mon, + int vlan, + const char *netname); #endif /* QEMU_MONITOR_TEXT_H */ -- 1.6.2.5

On Tue, Nov 03, 2009 at 02:50:01PM -0500, Daniel P. Berrange wrote:
Change the QEMU driver to not directly invoke the text mode monitor APIs. Instead add a generic wrapper layer, which will eventually invoke either the text or JSON protocol code as needed. Pass an qemuMonitorPtr object into the monitor APIs instead of virDomainObjPtr to complete the de-coupling of the monitor impl from virDomainObj data structures
* src/qemu/qemu_conf.h: Remove qemuDomainObjPrivate definition * src/qemu/qemu_driver.c: Add qemuDomainObjPrivate definition. Pass qemuMonitorPtr into all monitor APIs instead of the virDomainObjPtr instance. * src/qemu/qemu_monitor.c, src/qemu/qemu_monitor.h: Add thin wrappers for all qemuMonitorXXX command APIs, calling into qemu_monitor_text.c/h * src/qemu/qemu_monitor_text.c, src/qemu/qemu_monitor_text.h: Rename qemuMonitor -> qemuMonitorText & update to accept qemuMonitorPtr instead of virDomainObjPtr [...] @@ -1192,7 +1199,7 @@ qemudInitCpus(virConnectPtr conn,
if (migrateFrom == NULL) { /* Allow the CPUS to start executing */ - if (qemuMonitorStartCPUs(conn, vm) < 0) { + if (qemuMonitorStartCPUs(priv->mon, conn) < 0) {
err args are really swapped now ? I gues steh compiled would complain loudly if this wasn't the case ...
- (qemuMonitorSetBalloon(vm, vm->def->memory) < 0) || + (qemuMonitorSetBalloon(((qemuDomainObjPrivatePtr)vm->privateData)->mon, vm->def->memory) < 0) ||
any possibility to wrap to 80 ?
if (vm->state == VIR_DOMAIN_PAUSED) { - if (qemuMonitorStartCPUs(dom->conn, vm) < 0) { + qemuDomainObjPrivatePtr priv = vm->privateData; + if (qemuMonitorStartCPUs(priv->mon, dom->conn) < 0) {
apparently yes swapped now ...
+ +int +qemuMonitorStartCPUs(qemuMonitorPtr mon, + virConnectPtr conn) +{ + DEBUG("mon=%p, fd=%d", mon, mon->fd); + + return qemuMonitorTextStartCPUs(mon, conn); +}
Okay ... qemuMonitorPtr is the first arg for all those entry points now, makes sense... ACK a fairly long patch but mostly automatic change and the addition of the indirection, nothing really surprizing :-) Daniel -- Daniel Veillard | libxml Gnome XML XSLT toolkit http://xmlsoft.org/ daniel@veillard.com | Rpmfind RPM search engine http://rpmfind.net/ http://veillard.com/ | virtualization library http://libvirt.org/

In preparation of the monitor I/O process becoming fully asynchronous, it is neccessary to ensure all access to internals of the qemuMonitorPtr object is protected by a mutex lock. * src/qemu/qemu_monitor.c, src/qemu/qemu_monitor.h: Add mutex for locking monitor. * src/qemu/qemu_driver.c: Add locking around all monitor commands --- src/qemu/qemu_driver.c | 299 +++++++++++++++++++++++++++++++++++------------ src/qemu/qemu_monitor.c | 19 +++ src/qemu/qemu_monitor.h | 3 + 3 files changed, 248 insertions(+), 73 deletions(-) diff --git a/src/qemu/qemu_driver.c b/src/qemu/qemu_driver.c index 0fbd20f..82cad69 100644 --- a/src/qemu/qemu_driver.c +++ b/src/qemu/qemu_driver.c @@ -141,6 +141,22 @@ static void qemuDomainObjPrivateFree(void *data) } +static void qemuDomainObjEnterMonitor(virDomainObjPtr obj) +{ + qemuDomainObjPrivatePtr priv = obj->privateData; + + qemuMonitorLock(priv->mon); +} + + +static void qemuDomainObjExitMonitor(virDomainObjPtr obj) +{ + qemuDomainObjPrivatePtr priv = obj->privateData; + + qemuMonitorUnlock(priv->mon); +} + + static int qemuCgroupControllerActive(struct qemud_driver *driver, int controller) { @@ -1139,8 +1155,12 @@ qemuDetectVcpuPIDs(virConnectPtr conn, /* What follows is now all KVM specific */ - if ((ncpupids = qemuMonitorGetCPUInfo(priv->mon, &cpupids)) < 0) + qemuDomainObjEnterMonitor(vm); + if ((ncpupids = qemuMonitorGetCPUInfo(priv->mon, &cpupids)) < 0) { + qemuDomainObjExitMonitor(vm); return -1; + } + qemuDomainObjExitMonitor(vm); /* Treat failure to get VCPU<->PID mapping as non-fatal */ if (ncpupids == 0) @@ -1197,14 +1217,18 @@ qemudInitCpus(virConnectPtr conn, } #endif /* HAVE_SCHED_GETAFFINITY */ + /* XXX This resume doesn't really belong here. Move it up to caller */ if (migrateFrom == NULL) { /* Allow the CPUS to start executing */ + qemuDomainObjEnterMonitor(vm); if (qemuMonitorStartCPUs(priv->mon, conn) < 0) { if (virGetLastError() == NULL) qemudReportError(conn, NULL, NULL, VIR_ERR_INTERNAL_ERROR, "%s", _("resume operation failed")); + qemuDomainObjExitMonitor(vm); return -1; } + qemuDomainObjExitMonitor(vm); } return 0; @@ -1221,10 +1245,12 @@ qemuInitPasswords(struct qemud_driver *driver, vm->def->graphics[0]->type == VIR_DOMAIN_GRAPHICS_TYPE_VNC && (vm->def->graphics[0]->data.vnc.passwd || driver->vncPassword)) { + qemuDomainObjEnterMonitor(vm); ret = qemuMonitorSetVNCPassword(priv->mon, vm->def->graphics[0]->data.vnc.passwd ? vm->def->graphics[0]->data.vnc.passwd : driver->vncPassword); + qemuDomainObjExitMonitor(vm); } return ret; @@ -1910,6 +1936,7 @@ static int qemudStartVMDaemon(virConnectPtr conn, char ebuf[1024]; char *pidfile = NULL; int logfile; + qemuDomainObjPrivatePtr priv = vm->privateData; struct qemudHookData hookData; hookData.conn = conn; @@ -2082,26 +2109,37 @@ static int qemudStartVMDaemon(virConnectPtr conn, VIR_FREE(tapfds); } - if (ret == -1) + if (ret == -1) /* The VM failed to start */ goto cleanup; - if ((qemudWaitForMonitor(conn, driver, vm, pos) < 0) || - (qemuDetectVcpuPIDs(conn, vm) < 0) || - (qemudInitCpus(conn, vm, migrateFrom) < 0) || - (qemuInitPasswords(driver, vm) < 0) || - (qemuMonitorSetBalloon(((qemuDomainObjPrivatePtr)vm->privateData)->mon, vm->def->memory) < 0) || - (virDomainSaveStatus(conn, driver->stateDir, vm) < 0)) { - qemudShutdownVMDaemon(conn, driver, vm); - ret = -1; - /* No need for 'goto cleanup' now since qemudShutdownVMDaemon does enough */ + if (qemudWaitForMonitor(conn, driver, vm, pos) < 0) + goto abort; + + if (qemuDetectVcpuPIDs(conn, vm) < 0) + goto abort; + + if (qemudInitCpus(conn, vm, migrateFrom) < 0) + goto abort; + + if (qemuInitPasswords(driver, vm) < 0) + goto abort; + + qemuDomainObjEnterMonitor(vm); + if (qemuMonitorSetBalloon(priv->mon, vm->def->memory) < 0) { + qemuDomainObjExitMonitor(vm); + goto abort; } + qemuDomainObjExitMonitor(vm); - if (logfile != -1) - close(logfile); + if (virDomainSaveStatus(conn, driver->stateDir, vm) < 0) + goto abort; - return ret; + return 0; cleanup: + /* We jump here if we failed to start the VM for any reason + * XXX investigate if we can kill this block and safely call + * qemudShutdownVMDaemon even though no PID is running */ if (vm->def->seclabel.type == VIR_DOMAIN_SECLABEL_DYNAMIC) { VIR_FREE(vm->def->seclabel.model); VIR_FREE(vm->def->seclabel.label); @@ -2116,6 +2154,16 @@ cleanup: close(logfile); vm->def->id = -1; return -1; + +abort: + /* We jump here if we failed to initialize the now running VM + * killing it off and pretend we never started it */ + qemudShutdownVMDaemon(conn, driver, vm); + + if (logfile != -1) + close(logfile); + + return -1; } @@ -2649,8 +2697,12 @@ static int qemudDomainSuspend(virDomainPtr dom) { } if (vm->state != VIR_DOMAIN_PAUSED) { qemuDomainObjPrivatePtr priv = vm->privateData; - if (qemuMonitorStopCPUs(priv->mon) < 0) + qemuDomainObjEnterMonitor(vm); + if (qemuMonitorStopCPUs(priv->mon) < 0) { + qemuDomainObjExitMonitor(vm); goto cleanup; + } + qemuDomainObjExitMonitor(vm); vm->state = VIR_DOMAIN_PAUSED; event = virDomainEventNewFromObj(vm, VIR_DOMAIN_EVENT_SUSPENDED, @@ -2694,12 +2746,15 @@ static int qemudDomainResume(virDomainPtr dom) { } if (vm->state == VIR_DOMAIN_PAUSED) { qemuDomainObjPrivatePtr priv = vm->privateData; + qemuDomainObjEnterMonitor(vm); if (qemuMonitorStartCPUs(priv->mon, dom->conn) < 0) { + qemuDomainObjExitMonitor(vm); if (virGetLastError() == NULL) qemudReportError(dom->conn, dom, NULL, VIR_ERR_OPERATION_FAILED, "%s", _("resume operation failed")); goto cleanup; } + qemuDomainObjExitMonitor(vm); vm->state = VIR_DOMAIN_RUNNING; event = virDomainEventNewFromObj(vm, VIR_DOMAIN_EVENT_RESUMED, @@ -2743,10 +2798,9 @@ static int qemudDomainShutdown(virDomainPtr dom) { } qemuDomainObjPrivatePtr priv = vm->privateData; - if (qemuMonitorSystemPowerdown(priv->mon) < 0) - goto cleanup; - - ret = 0; + qemuDomainObjEnterMonitor(vm); + ret = qemuMonitorSystemPowerdown(priv->mon); + qemuDomainObjExitMonitor(vm); cleanup: if (vm) @@ -2905,7 +2959,9 @@ static int qemudDomainSetMemory(virDomainPtr dom, unsigned long newmem) { if (virDomainObjIsActive(vm)) { qemuDomainObjPrivatePtr priv = vm->privateData; + qemuDomainObjEnterMonitor(vm); int r = qemuMonitorSetBalloon(priv->mon, newmem); + qemuDomainObjExitMonitor(vm); if (r < 0) goto cleanup; @@ -2960,7 +3016,9 @@ static int qemudDomainGetInfo(virDomainPtr dom, if (virDomainObjIsActive(vm)) { qemuDomainObjPrivatePtr priv = vm->privateData; + qemuDomainObjEnterMonitor(vm); err = qemuMonitorGetBalloonInfo(priv->mon, &balloon); + qemuDomainObjExitMonitor(vm); if (err < 0) goto cleanup; @@ -3069,8 +3127,12 @@ static int qemudDomainSave(virDomainPtr dom, if (vm->state == VIR_DOMAIN_RUNNING) { qemuDomainObjPrivatePtr priv = vm->privateData; header.was_running = 1; - if (qemuMonitorStopCPUs(priv->mon) < 0) + qemuDomainObjEnterMonitor(vm); + if (qemuMonitorStopCPUs(priv->mon) < 0) { + qemuDomainObjExitMonitor(vm); goto cleanup; + } + qemuDomainObjExitMonitor(vm); vm->state = VIR_DOMAIN_PAUSED; } @@ -3113,7 +3175,9 @@ static int qemudDomainSave(virDomainPtr dom, if (header.compressed == QEMUD_SAVE_FORMAT_RAW) { const char *args[] = { "cat", NULL }; qemuDomainObjPrivatePtr priv = vm->privateData; + qemuDomainObjEnterMonitor(vm); ret = qemuMonitorMigrateToCommand(priv->mon, 0, args, path); + qemuDomainObjExitMonitor(vm); } else { const char *prog = qemudSaveCompressionTypeToString(header.compressed); qemuDomainObjPrivatePtr priv = vm->privateData; @@ -3122,7 +3186,9 @@ static int qemudDomainSave(virDomainPtr dom, "-c", NULL }; + qemuDomainObjEnterMonitor(vm); ret = qemuMonitorMigrateToCommand(priv->mon, 0, args, path); + qemuDomainObjExitMonitor(vm); } if (ret < 0) @@ -3193,12 +3259,18 @@ static int qemudDomainCoreDump(virDomainPtr dom, /* Pause domain for non-live dump */ if (vm->state == VIR_DOMAIN_RUNNING) { - if (qemuMonitorStopCPUs(priv->mon) < 0) + qemuDomainObjEnterMonitor(vm); + if (qemuMonitorStopCPUs(priv->mon) < 0) { + qemuDomainObjExitMonitor(vm); goto cleanup; + } + qemuDomainObjExitMonitor(vm); paused = 1; } + qemuDomainObjEnterMonitor(vm); ret = qemuMonitorMigrateToCommand(priv->mon, 0, args, path); + qemuDomainObjExitMonitor(vm); paused = 1; cleanup: @@ -3206,11 +3278,13 @@ cleanup: will support synchronous operations so we always get here after the migration is complete. */ if (resume && paused) { + qemuDomainObjEnterMonitor(vm); if (qemuMonitorStartCPUs(priv->mon, dom->conn) < 0) { if (virGetLastError() == NULL) qemudReportError(dom->conn, dom, NULL, VIR_ERR_OPERATION_FAILED, "%s", _("resuming after dump failed")); } + qemuDomainObjExitMonitor(vm); } if (vm) virDomainObjUnlock(vm); @@ -3724,12 +3798,15 @@ static int qemudDomainRestore(virConnectPtr conn, /* If it was running before, resume it now. */ if (header.was_running) { qemuDomainObjPrivatePtr priv = vm->privateData; + qemuDomainObjEnterMonitor(vm); if (qemuMonitorStartCPUs(priv->mon, conn) < 0) { if (virGetLastError() == NULL) qemudReportError(conn, NULL, NULL, VIR_ERR_OPERATION_FAILED, "%s", _("failed to resume domain")); + qemuDomainObjExitMonitor(vm); goto cleanup; } + qemuDomainObjExitMonitor(vm); vm->state = VIR_DOMAIN_RUNNING; virDomainSaveStatus(conn, driver->stateDir, vm); } @@ -3772,7 +3849,9 @@ static char *qemudDomainDumpXML(virDomainPtr dom, /* Refresh current memory based on balloon info */ if (virDomainObjIsActive(vm)) { qemuDomainObjPrivatePtr priv = vm->privateData; + qemuDomainObjEnterMonitor(vm); err = qemuMonitorGetBalloonInfo(priv->mon, &balloon); + qemuDomainObjExitMonitor(vm); if (err < 0) goto cleanup; if (err > 0) @@ -4343,11 +4422,13 @@ static int qemudDomainChangeEjectableMedia(virConnectPtr conn, } qemuDomainObjPrivatePtr priv = vm->privateData; + qemuDomainObjEnterMonitor(vm); if (newdisk->src) { ret = qemuMonitorChangeMedia(priv->mon, devname, newdisk->src); } else { ret = qemuMonitorEjectMedia(priv->mon, devname); } + qemuDomainObjExitMonitor(vm); if (ret == 0) { VIR_FREE(origdisk->src); @@ -4364,7 +4445,7 @@ static int qemudDomainAttachPciDiskDevice(virConnectPtr conn, virDomainObjPtr vm, virDomainDeviceDefPtr dev) { - int i; + int i, ret; const char* type = virDomainDiskBusTypeToString(dev->data.disk->bus); qemuDomainObjPrivatePtr priv = vm->privateData; @@ -4381,17 +4462,19 @@ static int qemudDomainAttachPciDiskDevice(virConnectPtr conn, return -1; } - if (qemuMonitorAddPCIDisk(priv->mon, - dev->data.disk->src, - type, - &dev->data.disk->pci_addr.domain, - &dev->data.disk->pci_addr.bus, - &dev->data.disk->pci_addr.slot) < 0) - return -1; + qemuDomainObjEnterMonitor(vm); + ret = qemuMonitorAddPCIDisk(priv->mon, + dev->data.disk->src, + type, + &dev->data.disk->pci_addr.domain, + &dev->data.disk->pci_addr.bus, + &dev->data.disk->pci_addr.slot); + qemuDomainObjExitMonitor(vm); - virDomainDiskInsertPreAlloced(vm->def, dev->data.disk); + if (ret == 0) + virDomainDiskInsertPreAlloced(vm->def, dev->data.disk); - return 0; + return ret; } static int qemudDomainAttachUsbMassstorageDevice(virConnectPtr conn, @@ -4399,7 +4482,7 @@ static int qemudDomainAttachUsbMassstorageDevice(virConnectPtr conn, virDomainDeviceDefPtr dev) { qemuDomainObjPrivatePtr priv = vm->privateData; - int i; + int i, ret; for (i = 0 ; i < vm->def->ndisks ; i++) { if (STREQ(vm->def->disks[i]->dst, dev->data.disk->dst)) { @@ -4420,12 +4503,14 @@ static int qemudDomainAttachUsbMassstorageDevice(virConnectPtr conn, return -1; } - if (qemuMonitorAddUSBDisk(priv->mon, dev->data.disk->src) < 0) - return -1; + qemuDomainObjEnterMonitor(vm); + ret = qemuMonitorAddUSBDisk(priv->mon, dev->data.disk->src); + qemuDomainObjExitMonitor(vm); - virDomainDiskInsertPreAlloced(vm->def, dev->data.disk); + if (ret == 0) + virDomainDiskInsertPreAlloced(vm->def, dev->data.disk); - return 0; + return ret; } static int qemudDomainAttachNetDevice(virConnectPtr conn, @@ -4481,16 +4566,24 @@ static int qemudDomainAttachNetDevice(virConnectPtr conn, if (virAsprintf(&tapfd_name, "fd-%s", net->hostnet_name) < 0) goto no_memory; - if (qemuMonitorSendFileHandle(priv->mon, tapfd_name, tapfd) < 0) + qemuDomainObjEnterMonitor(vm); + if (qemuMonitorSendFileHandle(priv->mon, tapfd_name, tapfd) < 0) { + qemuDomainObjExitMonitor(vm); goto cleanup; + } + qemuDomainObjExitMonitor(vm); } if (qemuBuildHostNetStr(conn, net, ' ', net->vlan, tapfd_name, &netstr) < 0) goto try_tapfd_close; - if (qemuMonitorAddHostNetwork(priv->mon, netstr) < 0) + qemuDomainObjEnterMonitor(vm); + if (qemuMonitorAddHostNetwork(priv->mon, netstr) < 0) { + qemuDomainObjExitMonitor(vm); goto try_tapfd_close; + } + qemuDomainObjExitMonitor(vm); if (tapfd != -1) close(tapfd); @@ -4499,11 +4592,15 @@ static int qemudDomainAttachNetDevice(virConnectPtr conn, if (qemuBuildNicStr(conn, net, NULL, net->vlan, &nicstr) < 0) goto try_remove; + qemuDomainObjEnterMonitor(vm); if (qemuMonitorAddPCINetwork(priv->mon, nicstr, &net->pci_addr.domain, &net->pci_addr.bus, - &net->pci_addr.slot) < 0) + &net->pci_addr.slot) < 0) { + qemuDomainObjExitMonitor(vm); goto try_remove; + } + qemuDomainObjExitMonitor(vm); ret = 0; @@ -4521,15 +4618,22 @@ cleanup: try_remove: if (!net->hostnet_name || net->vlan == 0) VIR_WARN0(_("Unable to remove network backend\n")); - else if (qemuMonitorRemoveHostNetwork(priv->mon, net->vlan, net->hostnet_name) < 0) - VIR_WARN(_("Failed to remove network backend for vlan %d, net %s"), - net->vlan, net->hostnet_name); + else { + qemuDomainObjEnterMonitor(vm); + if (qemuMonitorRemoveHostNetwork(priv->mon, net->vlan, net->hostnet_name) < 0) + VIR_WARN(_("Failed to remove network backend for vlan %d, net %s"), + net->vlan, net->hostnet_name); + qemuDomainObjExitMonitor(vm); + } goto cleanup; try_tapfd_close: - if (tapfd_name && - qemuMonitorCloseFileHandle(priv->mon, tapfd_name) < 0) - VIR_WARN(_("Failed to close tapfd with '%s'\n"), tapfd_name); + if (tapfd_name) { + qemuDomainObjEnterMonitor(vm); + if (qemuMonitorCloseFileHandle(priv->mon, tapfd_name) < 0) + VIR_WARN(_("Failed to close tapfd with '%s'\n"), tapfd_name); + qemuDomainObjExitMonitor(vm); + } goto cleanup; @@ -4546,6 +4650,7 @@ static int qemudDomainAttachHostPciDevice(virConnectPtr conn, qemuDomainObjPrivatePtr priv = vm->privateData; virDomainHostdevDefPtr hostdev = dev->data.hostdev; pciDevice *pci; + int ret; if (VIR_REALLOC_N(vm->def->hostdevs, vm->def->nhostdevs+1) < 0) { virReportOOMError(conn); @@ -4571,14 +4676,17 @@ static int qemudDomainAttachHostPciDevice(virConnectPtr conn, return -1; } - if (qemuMonitorAddPCIHostDevice(priv->mon, - hostdev->source.subsys.u.pci.domain, - hostdev->source.subsys.u.pci.bus, - hostdev->source.subsys.u.pci.slot, - hostdev->source.subsys.u.pci.function, - &hostdev->source.subsys.u.pci.guest_addr.domain, - &hostdev->source.subsys.u.pci.guest_addr.bus, - &hostdev->source.subsys.u.pci.guest_addr.slot) < 0) + qemuDomainObjEnterMonitor(vm); + ret = qemuMonitorAddPCIHostDevice(priv->mon, + hostdev->source.subsys.u.pci.domain, + hostdev->source.subsys.u.pci.bus, + hostdev->source.subsys.u.pci.slot, + hostdev->source.subsys.u.pci.function, + &hostdev->source.subsys.u.pci.guest_addr.domain, + &hostdev->source.subsys.u.pci.guest_addr.bus, + &hostdev->source.subsys.u.pci.guest_addr.slot); + qemuDomainObjExitMonitor(vm); + if (ret < 0) goto error; vm->def->hostdevs[vm->def->nhostdevs++] = hostdev; @@ -4603,6 +4711,7 @@ static int qemudDomainAttachHostUsbDevice(virConnectPtr conn, return -1; } + qemuDomainObjEnterMonitor(vm); if (dev->data.hostdev->source.subsys.u.usb.vendor) { ret = qemuMonitorAddUSBDeviceMatch(priv->mon, dev->data.hostdev->source.subsys.u.usb.vendor, @@ -4612,6 +4721,7 @@ static int qemudDomainAttachHostUsbDevice(virConnectPtr conn, dev->data.hostdev->source.subsys.u.usb.bus, dev->data.hostdev->source.subsys.u.usb.device); } + qemuDomainObjExitMonitor(vm); if (ret != -1) vm->def->hostdevs[vm->def->nhostdevs++] = dev->data.hostdev; @@ -4804,11 +4914,15 @@ static int qemudDomainDetachPciDiskDevice(virConnectPtr conn, goto cleanup; } + qemuDomainObjEnterMonitor(vm); if (qemuMonitorRemovePCIDevice(priv->mon, detach->pci_addr.domain, detach->pci_addr.bus, - detach->pci_addr.slot) < 0) + detach->pci_addr.slot) < 0) { + qemuDomainObjExitMonitor(vm); goto cleanup; + } + qemuDomainObjExitMonitor(vm); if (vm->def->ndisks > 1) { memmove(vm->def->disks + i, @@ -4864,14 +4978,20 @@ qemudDomainDetachNetDevice(virConnectPtr conn, goto cleanup; } + qemuDomainObjEnterMonitor(vm); if (qemuMonitorRemovePCIDevice(priv->mon, detach->pci_addr.domain, detach->pci_addr.bus, - detach->pci_addr.slot) < 0) + detach->pci_addr.slot) < 0) { + qemuDomainObjExitMonitor(vm); goto cleanup; + } - if (qemuMonitorRemoveHostNetwork(priv->mon, detach->vlan, detach->hostnet_name) < 0) + if (qemuMonitorRemoveHostNetwork(priv->mon, detach->vlan, detach->hostnet_name) < 0) { + qemuDomainObjExitMonitor(vm); goto cleanup; + } + qemuDomainObjExitMonitor(vm); if (vm->def->nnets > 1) { memmove(vm->def->nets + i, @@ -4935,11 +5055,15 @@ static int qemudDomainDetachHostPciDevice(virConnectPtr conn, return -1; } + qemuDomainObjEnterMonitor(vm); if (qemuMonitorRemovePCIDevice(priv->mon, detach->source.subsys.u.pci.guest_addr.domain, detach->source.subsys.u.pci.guest_addr.bus, - detach->source.subsys.u.pci.guest_addr.slot) < 0) + detach->source.subsys.u.pci.guest_addr.slot) < 0) { + qemuDomainObjExitMonitor(vm); return -1; + } + qemuDomainObjExitMonitor(vm); ret = 0; @@ -5369,16 +5493,15 @@ qemudDomainBlockStats (virDomainPtr dom, goto cleanup; qemuDomainObjPrivatePtr priv = vm->privateData; - if (qemuMonitorGetBlockStatsInfo(priv->mon, - qemu_dev_name, - &stats->rd_req, - &stats->rd_bytes, - &stats->wr_req, - &stats->wr_bytes, - &stats->errs) < 0) - goto cleanup; - - ret = 0; + qemuDomainObjEnterMonitor(vm); + ret = qemuMonitorGetBlockStatsInfo(priv->mon, + qemu_dev_name, + &stats->rd_req, + &stats->rd_bytes, + &stats->wr_req, + &stats->wr_bytes, + &stats->errs); + qemuDomainObjExitMonitor(vm); cleanup: VIR_FREE(qemu_dev_name); @@ -5568,13 +5691,19 @@ qemudDomainMemoryPeek (virDomainPtr dom, } qemuDomainObjPrivatePtr priv = vm->privateData; + qemuDomainObjEnterMonitor(vm); if (flags == VIR_MEMORY_VIRTUAL) { - if (qemuMonitorSaveVirtualMemory(priv->mon, offset, size, tmp) < 0) + if (qemuMonitorSaveVirtualMemory(priv->mon, offset, size, tmp) < 0) { + qemuDomainObjExitMonitor(vm); goto cleanup; + } } else { - if (qemuMonitorSavePhysicalMemory(priv->mon, offset, size, tmp) < 0) + if (qemuMonitorSavePhysicalMemory(priv->mon, offset, size, tmp) < 0) { + qemuDomainObjExitMonitor(vm); goto cleanup; + } } + qemuDomainObjExitMonitor(vm); /* Read the memory file into buffer. */ if (saferead (fd, buffer, size) == (ssize_t) -1) { @@ -6346,12 +6475,17 @@ static int doNativeMigrate(virDomainPtr dom, goto cleanup; } + qemuDomainObjEnterMonitor(vm); if (resource > 0 && - qemuMonitorSetMigrationSpeed(priv->mon, resource) < 0) + qemuMonitorSetMigrationSpeed(priv->mon, resource) < 0) { + qemuDomainObjExitMonitor(vm); goto cleanup; + } - if (qemuMonitorMigrateToHost(priv->mon, 0, uribits->server, uribits->port) < 0) + if (qemuMonitorMigrateToHost(priv->mon, 0, uribits->server, uribits->port) < 0) { + qemuDomainObjExitMonitor(vm); goto cleanup; + } /* it is also possible that the migrate didn't fail initially, but * rather failed later on. Check the output of "info migrate" @@ -6361,8 +6495,10 @@ static int doNativeMigrate(virDomainPtr dom, &transferred, &remaining, &total) < 0) { + qemuDomainObjExitMonitor(vm); goto cleanup; } + qemuDomainObjExitMonitor(vm); if (status != QEMU_MONITOR_MIGRATION_STATUS_COMPLETED) { qemudReportError (dom->conn, dom, NULL, VIR_ERR_OPERATION_FAILED, @@ -6519,6 +6655,7 @@ static int doTunnelMigrate(virDomainPtr dom, goto cleanup; /* 3. start migration on source */ + qemuDomainObjEnterMonitor(vm); if (qemuCmdFlags & QEMUD_CMD_FLAG_MIGRATE_QEMU_UNIX) internalret = qemuMonitorMigrateToUnix(priv->mon, 1, unixfile); else if (qemuCmdFlags & QEMUD_CMD_FLAG_MIGRATE_QEMU_EXEC) { @@ -6527,6 +6664,7 @@ static int doTunnelMigrate(virDomainPtr dom, } else { internalret = -1; } + qemuDomainObjExitMonitor(vm); if (internalret < 0) { qemudReportError(dom->conn, dom, NULL, VIR_ERR_OPERATION_FAILED, "%s", _("tunnelled migration monitor command failed")); @@ -6539,13 +6677,16 @@ static int doTunnelMigrate(virDomainPtr dom, /* it is also possible that the migrate didn't fail initially, but * rather failed later on. Check the output of "info migrate" */ + qemuDomainObjEnterMonitor(vm); if (qemuMonitorGetMigrationStatus(priv->mon, &status, &transferred, &remaining, &total) < 0) { + qemuDomainObjExitMonitor(vm); goto cancel; } + qemuDomainObjExitMonitor(vm); if (status == QEMU_MONITOR_MIGRATION_STATUS_ERROR) { qemudReportError(dom->conn, dom, NULL, VIR_ERR_OPERATION_FAILED, @@ -6565,8 +6706,11 @@ static int doTunnelMigrate(virDomainPtr dom, retval = doTunnelSendAll(dom, st, client_sock); cancel: - if (retval != 0) + if (retval != 0) { + qemuDomainObjEnterMonitor(vm); qemuMonitorMigrateCancel(priv->mon); + qemuDomainObjExitMonitor(vm); + } finish: dname = dname ? dname : dom->name; @@ -6729,8 +6873,12 @@ qemudDomainMigratePerform (virDomainPtr dom, if (!(flags & VIR_MIGRATE_LIVE)) { qemuDomainObjPrivatePtr priv = vm->privateData; /* Pause domain for non-live migration */ - if (qemuMonitorStopCPUs(priv->mon) < 0) + qemuDomainObjEnterMonitor(vm); + if (qemuMonitorStopCPUs(priv->mon) < 0) { + qemuDomainObjExitMonitor(vm); goto cleanup; + } + qemuDomainObjExitMonitor(vm); paused = 1; event = virDomainEventNewFromObj(vm, @@ -6768,6 +6916,7 @@ cleanup: if (paused) { qemuDomainObjPrivatePtr priv = vm->privateData; /* we got here through some sort of failure; start the domain again */ + qemuDomainObjEnterMonitor(vm); if (qemuMonitorStartCPUs(priv->mon, dom->conn) < 0) { /* Hm, we already know we are in error here. We don't want to * overwrite the previous error, though, so we just throw something @@ -6776,6 +6925,7 @@ cleanup: VIR_ERROR(_("Failed to resume guest %s after failure\n"), vm->def->name); } + qemuDomainObjExitMonitor(vm); event = virDomainEventNewFromObj(vm, VIR_DOMAIN_EVENT_RESUMED, @@ -6853,12 +7003,15 @@ qemudDomainMigrateFinish2 (virConnectPtr dconn, * >= 0.10.6 to work properly. This isn't strictly necessary on * older qemu's, but it also doesn't hurt anything there */ + qemuDomainObjEnterMonitor(vm); if (qemuMonitorStartCPUs(priv->mon, dconn) < 0) { if (virGetLastError() == NULL) qemudReportError(dconn, NULL, NULL, VIR_ERR_INTERNAL_ERROR, "%s", _("resume operation failed")); + qemuDomainObjExitMonitor(vm); goto cleanup; } + qemuDomainObjExitMonitor(vm); vm->state = VIR_DOMAIN_RUNNING; event = virDomainEventNewFromObj(vm, diff --git a/src/qemu/qemu_monitor.c b/src/qemu/qemu_monitor.c index 46d5d9f..fd1c5e9 100644 --- a/src/qemu/qemu_monitor.c +++ b/src/qemu/qemu_monitor.c @@ -39,6 +39,8 @@ #define VIR_FROM_THIS VIR_FROM_QEMU struct _qemuMonitor { + virMutex lock; + int fd; int watch; int hasSendFD; @@ -49,6 +51,16 @@ struct _qemuMonitor { qemuMonitorDiskSecretLookup secretCB; }; +void qemuMonitorLock(qemuMonitorPtr mon) +{ + virMutexLock(&mon->lock); +} + +void qemuMonitorUnlock(qemuMonitorPtr mon) +{ + virMutexUnlock(&mon->lock); +} + /* Return -1 for error, 1 to continue reading and 0 for success */ typedef int qemuMonitorHandleOutput(virDomainObjPtr vm, const char *output); @@ -292,6 +304,12 @@ qemuMonitorOpen(virDomainObjPtr vm, return NULL; } + if (virMutexInit(&mon->lock) < 0) { + qemudReportError(NULL, NULL, NULL, VIR_ERR_INTERNAL_ERROR, "%s", + _("cannot initialize monitor mutex")); + VIR_FREE(mon); + return NULL; + } mon->fd = -1; mon->vm = vm; mon->eofCB = eofCB; @@ -343,6 +361,7 @@ void qemuMonitorClose(qemuMonitorPtr mon) if (mon->fd != -1) close(mon->fd); + virMutexDestroy(&mon->lock); VIR_FREE(mon); } diff --git a/src/qemu/qemu_monitor.h b/src/qemu/qemu_monitor.h index 5f06155..a8d517c 100644 --- a/src/qemu/qemu_monitor.h +++ b/src/qemu/qemu_monitor.h @@ -54,6 +54,9 @@ qemuMonitorPtr qemuMonitorOpen(virDomainObjPtr vm, void qemuMonitorClose(qemuMonitorPtr mon); +void qemuMonitorLock(qemuMonitorPtr mon); +void qemuMonitorUnlock(qemuMonitorPtr mon); + void qemuMonitorRegisterDiskSecretLookup(qemuMonitorPtr mon, qemuMonitorDiskSecretLookup secretCB); -- 1.6.2.5

On Tue, Nov 03, 2009 at 02:50:02PM -0500, Daniel P. Berrange wrote:
In preparation of the monitor I/O process becoming fully asynchronous, it is neccessary to ensure all access to internals of the qemuMonitorPtr object is protected by a mutex lock.
* src/qemu/qemu_monitor.c, src/qemu/qemu_monitor.h: Add mutex for locking monitor. * src/qemu/qemu_driver.c: Add locking around all monitor commands --- src/qemu/qemu_driver.c | 299 +++++++++++++++++++++++++++++++++++------------ src/qemu/qemu_monitor.c | 19 +++ src/qemu/qemu_monitor.h | 3 + 3 files changed, 248 insertions(+), 73 deletions(-)
[..]
+++ b/src/qemu/qemu_driver.c @@ -141,6 +141,22 @@ static void qemuDomainObjPrivateFree(void *data) }
+static void qemuDomainObjEnterMonitor(virDomainObjPtr obj) +{ + qemuDomainObjPrivatePtr priv = obj->privateData; + + qemuMonitorLock(priv->mon); +} + + +static void qemuDomainObjExitMonitor(virDomainObjPtr obj) +{ + qemuDomainObjPrivatePtr priv = obj->privateData; + + qemuMonitorUnlock(priv->mon); +} +
so we're using pthread mutex here and basically there is no way this could go wrong and there is no need to handle errors, okay
static int qemuCgroupControllerActive(struct qemud_driver *driver, int controller) {
the patch is quite regular
+ qemuDomainObjEnterMonitor(vm); + if (qemuMonitorStopCPUs(priv->mon) < 0) { + qemuDomainObjExitMonitor(vm); goto cleanup; + } + qemuDomainObjExitMonitor(vm);
to some extend I wonder if we shouldn't kill all those blocks and always use the more regular form qemuDomainObjEnterMonitor(vm); ret = qemuMonitorStopCPUs(priv->mon); qemuDomainObjExitMonitor(vm); if (ret < 0) goto cleanup; it's simpler, easier to read, we don't have to think about matching of Enter and Exit, and the generated code probably will be identical In a number of places the refactoring introduced the later way but the former is still present in a number of place, closer to original code but doesn't make the patch safer I'm afraid. The only inconvenience is the declaration of the temporary variable ... [...]
+void qemuMonitorLock(qemuMonitorPtr mon) +{ + virMutexLock(&mon->lock); +} + +void qemuMonitorUnlock(qemuMonitorPtr mon) +{ + virMutexUnlock(&mon->lock); +}
okay no error possible ... Okay, I didn't spot anything suspect in the locking patch. But as usual the problems may be in parts we may have forgotten about. Also sometime the patch shows only a fraction of the relevant code. Best is to push and test, ACK ! Daniel -- Daniel Veillard | libxml Gnome XML XSLT toolkit http://xmlsoft.org/ daniel@veillard.com | Rpmfind RPM search engine http://rpmfind.net/ http://veillard.com/ | virtualization library http://libvirt.org/

On Wed, Nov 04, 2009 at 05:51:36PM +0100, Daniel Veillard wrote:
On Tue, Nov 03, 2009 at 02:50:02PM -0500, Daniel P. Berrange wrote:
In preparation of the monitor I/O process becoming fully asynchronous, it is neccessary to ensure all access to internals of the qemuMonitorPtr object is protected by a mutex lock.
* src/qemu/qemu_monitor.c, src/qemu/qemu_monitor.h: Add mutex for locking monitor. * src/qemu/qemu_driver.c: Add locking around all monitor commands --- src/qemu/qemu_driver.c | 299 +++++++++++++++++++++++++++++++++++------------ src/qemu/qemu_monitor.c | 19 +++ src/qemu/qemu_monitor.h | 3 + 3 files changed, 248 insertions(+), 73 deletions(-)
[..]
+++ b/src/qemu/qemu_driver.c @@ -141,6 +141,22 @@ static void qemuDomainObjPrivateFree(void *data) }
+static void qemuDomainObjEnterMonitor(virDomainObjPtr obj) +{ + qemuDomainObjPrivatePtr priv = obj->privateData; + + qemuMonitorLock(priv->mon); +} + + +static void qemuDomainObjExitMonitor(virDomainObjPtr obj) +{ + qemuDomainObjPrivatePtr priv = obj->privateData; + + qemuMonitorUnlock(priv->mon); +} +
so we're using pthread mutex here and basically there is no way this could go wrong and there is no need to handle errors, okay
static int qemuCgroupControllerActive(struct qemud_driver *driver, int controller) {
the patch is quite regular
+ qemuDomainObjEnterMonitor(vm); + if (qemuMonitorStopCPUs(priv->mon) < 0) { + qemuDomainObjExitMonitor(vm); goto cleanup; + } + qemuDomainObjExitMonitor(vm);
to some extend I wonder if we shouldn't kill all those blocks and always use the more regular form
qemuDomainObjEnterMonitor(vm); ret = qemuMonitorStopCPUs(priv->mon); qemuDomainObjExitMonitor(vm); if (ret < 0) goto cleanup;
it's simpler, easier to read, we don't have to think about matching of Enter and Exit, and the generated code probably will be identical In a number of places the refactoring introduced the later way but the former is still present in a number of place, closer to original code but doesn't make the patch safer I'm afraid. The only inconvenience is the declaration of the temporary variable ...
Yes, I think I'll do a further add-on patch to make that change. Daniel -- |: Red Hat, Engineering, London -o- http://people.redhat.com/berrange/ :| |: http://libvirt.org -o- http://virt-manager.org -o- http://ovirt.org :| |: http://autobuild.org -o- http://search.cpan.org/~danberr/ :| |: GnuPG: 7D3B9505 -o- F3C9 553F A1DA 4AC2 5648 23C1 B3DF F742 7D3B 9505 :|

Add reference counting on the virDomainObjPtr objects. With the forthcoming asynchronous QEMU monitor, it will be neccessary to release the lock on virDomainObjPtr while waiting for a monitor command response. It is neccessary to ensure one thread can't delete a virDomainObjPtr while another is waiting. By introducing reference counting threads can make sure objects they are using are not accidentally deleted while unlocked. * src/conf/domain_conf.h, src/conf/domain_conf.c: Add virDomainObjRef/Unref APIs, remove virDomainObjFree * src/openvz/openvz_conf.c: replace call to virDomainObjFree with virDomainObjUnref --- src/conf/domain_conf.c | 32 ++++++++++++++++++++++++++++---- src/conf/domain_conf.h | 5 ++++- src/libvirt_private.syms | 3 ++- src/openvz/openvz_conf.c | 6 +++++- 4 files changed, 39 insertions(+), 7 deletions(-) diff --git a/src/conf/domain_conf.c b/src/conf/domain_conf.c index a9c8573..79bb274 100644 --- a/src/conf/domain_conf.c +++ b/src/conf/domain_conf.c @@ -219,7 +219,9 @@ int virDomainObjListInit(virDomainObjListPtr doms) static void virDomainObjListDeallocator(void *payload, const char *name ATTRIBUTE_UNUSED) { virDomainObjPtr obj = payload; - virDomainObjFree(obj); + virDomainObjLock(obj); + if (!virDomainObjUnref(obj)) + virDomainObjUnlock(obj); } void virDomainObjListDeinit(virDomainObjListPtr doms) @@ -582,11 +584,12 @@ void virDomainDefFree(virDomainDefPtr def) #ifndef PROXY -void virDomainObjFree(virDomainObjPtr dom) +static void virDomainObjFree(virDomainObjPtr dom) { if (!dom) return; + VIR_DEBUG("obj=%p", dom); virDomainDefFree(dom->def); virDomainDefFree(dom->newDef); @@ -602,6 +605,25 @@ void virDomainObjFree(virDomainObjPtr dom) VIR_FREE(dom); } +void virDomainObjRef(virDomainObjPtr dom) +{ + dom->refs++; + VIR_DEBUG("obj=%p refs=%d", dom, dom->refs); +} + + +int virDomainObjUnref(virDomainObjPtr dom) +{ + dom->refs--; + VIR_DEBUG("obj=%p refs=%d", dom, dom->refs); + if (dom->refs == 0) { + virDomainObjUnlock(dom); + virDomainObjFree(dom); + return 1; + } + return 0; +} + static virDomainObjPtr virDomainObjNew(virConnectPtr conn, virCapsPtr caps) { @@ -633,7 +655,9 @@ static virDomainObjPtr virDomainObjNew(virConnectPtr conn, domain->state = VIR_DOMAIN_SHUTOFF; domain->monitorWatch = -1; domain->monitor = -1; + domain->refs = 1; + VIR_DEBUG("obj=%p", domain); return domain; } @@ -3305,7 +3329,7 @@ static virDomainObjPtr virDomainObjParseXML(virConnectPtr conn, error: VIR_FREE(nodes); virDomainChrDefFree(obj->monitor_chr); - virDomainObjFree(obj); + virDomainObjUnref(obj); return NULL; } @@ -4846,7 +4870,7 @@ static virDomainObjPtr virDomainLoadStatus(virConnectPtr conn, return obj; error: - virDomainObjFree(obj); + virDomainObjUnref(obj); VIR_FREE(statusFile); return NULL; } diff --git a/src/conf/domain_conf.h b/src/conf/domain_conf.h index 8599ee7..675a49b 100644 --- a/src/conf/domain_conf.h +++ b/src/conf/domain_conf.h @@ -621,6 +621,7 @@ typedef struct _virDomainObj virDomainObj; typedef virDomainObj *virDomainObjPtr; struct _virDomainObj { virMutex lock; + int refs; int monitor; virDomainChrDefPtr monitor_chr; @@ -678,7 +679,9 @@ void virDomainVideoDefFree(virDomainVideoDefPtr def); void virDomainHostdevDefFree(virDomainHostdevDefPtr def); void virDomainDeviceDefFree(virDomainDeviceDefPtr def); void virDomainDefFree(virDomainDefPtr vm); -void virDomainObjFree(virDomainObjPtr vm); +void virDomainObjRef(virDomainObjPtr vm); +/* Returns 1 if the object was freed, 0 if more refs exist */ +int virDomainObjUnref(virDomainObjPtr vm); virDomainObjPtr virDomainAssignDef(virConnectPtr conn, virCapsPtr caps, diff --git a/src/libvirt_private.syms b/src/libvirt_private.syms index 600dfee..52f1a05 100644 --- a/src/libvirt_private.syms +++ b/src/libvirt_private.syms @@ -123,7 +123,6 @@ virDomainLifecycleTypeToString; virDomainLoadAllConfigs; virDomainNetDefFree; virDomainNetTypeToString; -virDomainObjFree; virDomainRemoveInactive; virDomainSaveXML; virDomainSaveConfig; @@ -151,6 +150,8 @@ virDomainObjListGetActiveIDs; virDomainObjListNumOfDomains; virDomainObjListInit; virDomainObjListDeinit; +virDomainObjRef; +virDomainObjUnref; # domain_event.h diff --git a/src/openvz/openvz_conf.c b/src/openvz/openvz_conf.c index c928afb..c2a39d0 100644 --- a/src/openvz/openvz_conf.c +++ b/src/openvz/openvz_conf.c @@ -463,6 +463,8 @@ int openvzLoadDomains(struct openvz_driver *driver) { goto cleanup; } + virDomainObjLock(dom); + if (VIR_ALLOC(dom->def) < 0) goto no_memory; @@ -471,6 +473,7 @@ int openvzLoadDomains(struct openvz_driver *driver) { else dom->state = VIR_DOMAIN_RUNNING; + dom->refs = 1; dom->pid = veid; dom->def->id = dom->state == VIR_DOMAIN_SHUTOFF ? -1 : veid; @@ -513,6 +516,7 @@ int openvzLoadDomains(struct openvz_driver *driver) { if (virHashAddEntry(driver->domains.objs, uuidstr, dom) < 0) goto no_memory; + virDomainObjUnlock(dom); dom = NULL; } @@ -525,7 +529,7 @@ int openvzLoadDomains(struct openvz_driver *driver) { cleanup: fclose(fp); - virDomainObjFree(dom); + virDomainObjUnref(dom); return -1; } -- 1.6.2.5

On Tue, Nov 03, 2009 at 02:50:03PM -0500, Daniel P. Berrange wrote:
Add reference counting on the virDomainObjPtr objects. With the forthcoming asynchronous QEMU monitor, it will be neccessary to release the lock on virDomainObjPtr while waiting for a monitor command response. It is neccessary to ensure one thread can't delete a virDomainObjPtr while another is waiting. By introducing reference counting threads can make sure objects they are using are not accidentally deleted while unlocked.
* src/conf/domain_conf.h, src/conf/domain_conf.c: Add virDomainObjRef/Unref APIs, remove virDomainObjFree * src/openvz/openvz_conf.c: replace call to virDomainObjFree with virDomainObjUnref --- src/conf/domain_conf.c | 32 ++++++++++++++++++++++++++++---- src/conf/domain_conf.h | 5 ++++- src/libvirt_private.syms | 3 ++- src/openvz/openvz_conf.c | 6 +++++- 4 files changed, 39 insertions(+), 7 deletions(-)
diff --git a/src/conf/domain_conf.c b/src/conf/domain_conf.c index a9c8573..79bb274 100644 --- a/src/conf/domain_conf.c +++ b/src/conf/domain_conf.c @@ -219,7 +219,9 @@ int virDomainObjListInit(virDomainObjListPtr doms) static void virDomainObjListDeallocator(void *payload, const char *name ATTRIBUTE_UNUSED) { virDomainObjPtr obj = payload; - virDomainObjFree(obj); + virDomainObjLock(obj); + if (!virDomainObjUnref(obj)) + virDomainObjUnlock(obj); }
void virDomainObjListDeinit(virDomainObjListPtr doms) @@ -582,11 +584,12 @@ void virDomainDefFree(virDomainDefPtr def)
#ifndef PROXY
-void virDomainObjFree(virDomainObjPtr dom) +static void virDomainObjFree(virDomainObjPtr dom) { if (!dom) return;
+ VIR_DEBUG("obj=%p", dom); virDomainDefFree(dom->def); virDomainDefFree(dom->newDef);
@@ -602,6 +605,25 @@ void virDomainObjFree(virDomainObjPtr dom) VIR_FREE(dom); }
+void virDomainObjRef(virDomainObjPtr dom) +{ + dom->refs++; + VIR_DEBUG("obj=%p refs=%d", dom, dom->refs); +} + + +int virDomainObjUnref(virDomainObjPtr dom) +{ + dom->refs--; + VIR_DEBUG("obj=%p refs=%d", dom, dom->refs); + if (dom->refs == 0) { + virDomainObjUnlock(dom); + virDomainObjFree(dom); + return 1; + } + return 0; +} + static virDomainObjPtr virDomainObjNew(virConnectPtr conn, virCapsPtr caps) { @@ -633,7 +655,9 @@ static virDomainObjPtr virDomainObjNew(virConnectPtr conn, domain->state = VIR_DOMAIN_SHUTOFF; domain->monitorWatch = -1; domain->monitor = -1; + domain->refs = 1;
+ VIR_DEBUG("obj=%p", domain); return domain; }
@@ -3305,7 +3329,7 @@ static virDomainObjPtr virDomainObjParseXML(virConnectPtr conn, error: VIR_FREE(nodes); virDomainChrDefFree(obj->monitor_chr); - virDomainObjFree(obj); + virDomainObjUnref(obj); return NULL; }
@@ -4846,7 +4870,7 @@ static virDomainObjPtr virDomainLoadStatus(virConnectPtr conn, return obj;
error: - virDomainObjFree(obj); + virDomainObjUnref(obj); VIR_FREE(statusFile); return NULL; } diff --git a/src/conf/domain_conf.h b/src/conf/domain_conf.h index 8599ee7..675a49b 100644 --- a/src/conf/domain_conf.h +++ b/src/conf/domain_conf.h @@ -621,6 +621,7 @@ typedef struct _virDomainObj virDomainObj; typedef virDomainObj *virDomainObjPtr; struct _virDomainObj { virMutex lock; + int refs;
int monitor; virDomainChrDefPtr monitor_chr; @@ -678,7 +679,9 @@ void virDomainVideoDefFree(virDomainVideoDefPtr def); void virDomainHostdevDefFree(virDomainHostdevDefPtr def); void virDomainDeviceDefFree(virDomainDeviceDefPtr def); void virDomainDefFree(virDomainDefPtr vm); -void virDomainObjFree(virDomainObjPtr vm); +void virDomainObjRef(virDomainObjPtr vm); +/* Returns 1 if the object was freed, 0 if more refs exist */ +int virDomainObjUnref(virDomainObjPtr vm);
virDomainObjPtr virDomainAssignDef(virConnectPtr conn, virCapsPtr caps, diff --git a/src/libvirt_private.syms b/src/libvirt_private.syms index 600dfee..52f1a05 100644 --- a/src/libvirt_private.syms +++ b/src/libvirt_private.syms @@ -123,7 +123,6 @@ virDomainLifecycleTypeToString; virDomainLoadAllConfigs; virDomainNetDefFree; virDomainNetTypeToString; -virDomainObjFree; virDomainRemoveInactive; virDomainSaveXML; virDomainSaveConfig; @@ -151,6 +150,8 @@ virDomainObjListGetActiveIDs; virDomainObjListNumOfDomains; virDomainObjListInit; virDomainObjListDeinit; +virDomainObjRef; +virDomainObjUnref;
# domain_event.h diff --git a/src/openvz/openvz_conf.c b/src/openvz/openvz_conf.c index c928afb..c2a39d0 100644 --- a/src/openvz/openvz_conf.c +++ b/src/openvz/openvz_conf.c @@ -463,6 +463,8 @@ int openvzLoadDomains(struct openvz_driver *driver) { goto cleanup; }
+ virDomainObjLock(dom); + if (VIR_ALLOC(dom->def) < 0) goto no_memory;
@@ -471,6 +473,7 @@ int openvzLoadDomains(struct openvz_driver *driver) { else dom->state = VIR_DOMAIN_RUNNING;
+ dom->refs = 1; dom->pid = veid; dom->def->id = dom->state == VIR_DOMAIN_SHUTOFF ? -1 : veid;
@@ -513,6 +516,7 @@ int openvzLoadDomains(struct openvz_driver *driver) { if (virHashAddEntry(driver->domains.objs, uuidstr, dom) < 0) goto no_memory;
+ virDomainObjUnlock(dom); dom = NULL; }
@@ -525,7 +529,7 @@ int openvzLoadDomains(struct openvz_driver *driver) {
cleanup: fclose(fp); - virDomainObjFree(dom); + virDomainObjUnref(dom); return -1; }
ACK, Daniel -- Daniel Veillard | libxml Gnome XML XSLT toolkit http://xmlsoft.org/ daniel@veillard.com | Rpmfind RPM search engine http://rpmfind.net/ http://veillard.com/ | virtualization library http://libvirt.org/

Change the QEMU monitor file handle watch to poll for both read & write events, as well as EOF. All I/O to/from the QEMU monitor FD is now done in the event callback thread. When the QEMU driver needs to send a command, it puts the data to be sent into a qemuMonitorMessagePtr object instance, queues it for dispatch, and then goes to sleep on a condition variable. The event thread sends all the data, and then waits for the reply to arrive, putting the response / error data back into the qemuMonitorMessagePtr and notifying the condition variable. * src/qemu/qemu_driver.c: XXX this change shouldn't be here * src/qemu/qemu_monitor.c, src/qemu/qemu_monitor.h: Remove raw I/O functions, and a generic qemuMonitorSend() for invoking a command * src/qemu/qemu_monitor_text.c, src/qemu/qemu_monitor_text.h: Remove all low level I/O, and use the new qemuMonitorSend() API. Provide a qemuMonitorTextIOProcess() method for detecting command/reply/prompt boundaries in the monitor data stream --- .x-sc_avoid_write | 1 + src/conf/domain_conf.c | 3 +- src/qemu/qemu_driver.c | 64 ++++-- src/qemu/qemu_monitor.c | 549 ++++++++++++++++++++++++++---------------- src/qemu/qemu_monitor.h | 46 +++-- src/qemu/qemu_monitor_text.c | 402 +++++++++++++++++-------------- src/qemu/qemu_monitor_text.h | 5 + 7 files changed, 650 insertions(+), 420 deletions(-) diff --git a/.x-sc_avoid_write b/.x-sc_avoid_write index 111fb28..1f893b8 100644 --- a/.x-sc_avoid_write +++ b/.x-sc_avoid_write @@ -1,5 +1,6 @@ ^src/libvirt\.c$ ^src/qemu/qemu_driver\.c$ +^src/qemu/qemu_monitor\.c$ ^src/util/util\.c$ ^src/xen/xend_internal\.c$ ^daemon/libvirtd.c$ diff --git a/src/conf/domain_conf.c b/src/conf/domain_conf.c index 79bb274..b42fdc5 100644 --- a/src/conf/domain_conf.c +++ b/src/conf/domain_conf.c @@ -4870,7 +4870,8 @@ static virDomainObjPtr virDomainLoadStatus(virConnectPtr conn, return obj; error: - virDomainObjUnref(obj); + if (obj) + virDomainObjUnref(obj); VIR_FREE(statusFile); return NULL; } diff --git a/src/qemu/qemu_driver.c b/src/qemu/qemu_driver.c index 82cad69..fea439b 100644 --- a/src/qemu/qemu_driver.c +++ b/src/qemu/qemu_driver.c @@ -345,9 +345,8 @@ qemuHandleMonitorEOF(qemuMonitorPtr mon ATTRIBUTE_UNUSED, struct qemud_driver *driver = qemu_driver; virDomainEventPtr event = NULL; - qemuDriverLock(driver); + VIR_DEBUG("Received EOF on %p '%s'", vm, vm->def->name); virDomainObjLock(vm); - qemuDriverUnlock(driver); event = virDomainEventNewFromObj(vm, VIR_DOMAIN_EVENT_STOPPED, @@ -411,11 +410,24 @@ findVolumeQcowPassphrase(qemuMonitorPtr mon ATTRIBUTE_UNUSED, char *passphrase; unsigned char *data; size_t size; + int ret = -1; + + /* XXX + * We ought to be taking the lock here, but that would + * require that it be released when monitor commands are + * run. Currently we deadlock if we try to take it again + * + * Until this is resolved, don't take the lock and rely + * on fact that the thread invoking this callback is + * running lock-step with the thread holding the lock + * + * virDomainObjLock(vm); + */ if (!conn) { qemudReportError(NULL, NULL, NULL, VIR_ERR_NO_SUPPORT, "%s", _("cannot find secrets without a connection")); - return -1; + goto cleanup; } if (conn->secretDriver == NULL || @@ -423,7 +435,7 @@ findVolumeQcowPassphrase(qemuMonitorPtr mon ATTRIBUTE_UNUSED, conn->secretDriver->getValue == NULL) { qemudReportError(conn, NULL, NULL, VIR_ERR_NO_SUPPORT, "%s", _("secret storage not supported")); - return -1; + goto cleanup; } enc = findDomainDiskEncryption(conn, vm, path); @@ -436,18 +448,18 @@ findVolumeQcowPassphrase(qemuMonitorPtr mon ATTRIBUTE_UNUSED, VIR_STORAGE_ENCRYPTION_SECRET_TYPE_PASSPHRASE) { qemudReportError(conn, NULL, NULL, VIR_ERR_INVALID_DOMAIN, _("invalid <encryption> for volume %s"), path); - return -1; + goto cleanup; } secret = conn->secretDriver->lookupByUUID(conn, enc->secrets[0]->uuid); if (secret == NULL) - return -1; + goto cleanup; data = conn->secretDriver->getValue(secret, &size, VIR_SECRET_GET_VALUE_INTERNAL_CALL); virUnrefSecret(secret); if (data == NULL) - return -1; + goto cleanup; if (memchr(data, '\0', size) != NULL) { memset(data, 0, size); @@ -455,14 +467,14 @@ findVolumeQcowPassphrase(qemuMonitorPtr mon ATTRIBUTE_UNUSED, qemudReportError(conn, NULL, NULL, VIR_ERR_INVALID_SECRET, _("format='qcow' passphrase for %s must not contain a " "'\\0'"), path); - return -1; + goto cleanup; } if (VIR_ALLOC_N(passphrase, size + 1) < 0) { memset(data, 0, size); VIR_FREE(data); virReportOOMError(conn); - return -1; + goto cleanup; } memcpy(passphrase, data, size); passphrase[size] = '\0'; @@ -473,15 +485,24 @@ findVolumeQcowPassphrase(qemuMonitorPtr mon ATTRIBUTE_UNUSED, *secretRet = passphrase; *secretLen = size; - return 0; + ret = 0; + +cleanup: + /* + * XXX + * See earlier comment about lock + * + * virDomainObjUnlock(vm); + */ + return ret; } static int -qemuConnectMonitor(virDomainObjPtr vm, int reconnect) +qemuConnectMonitor(virDomainObjPtr vm) { qemuDomainObjPrivatePtr priv = vm->privateData; - if ((priv->mon = qemuMonitorOpen(vm, reconnect, qemuHandleMonitorEOF)) == NULL) { + if ((priv->mon = qemuMonitorOpen(vm, qemuHandleMonitorEOF)) == NULL) { VIR_ERROR(_("Failed to connect monitor for %s\n"), vm->def->name); return -1; } @@ -504,7 +525,10 @@ qemuReconnectDomain(void *payload, const char *name ATTRIBUTE_UNUSED, void *opaq virDomainObjLock(obj); - if (qemuConnectMonitor(obj, 1) < 0) + VIR_DEBUG("Reconnect monitor to %p '%s'", obj, obj->def->name); + + /* XXX check PID liveliness & EXE path */ + if (qemuConnectMonitor(obj) < 0) goto error; if (qemuUpdateActivePciHostdevs(driver, obj->def) < 0) { @@ -528,7 +552,10 @@ error: * to remove danger of it ending up running twice if * user tries to start it again later */ qemudShutdownVMDaemon(NULL, driver, obj); - virDomainObjUnlock(obj); + if (!obj->persistent) + virDomainRemoveInactive(&driver->domains, obj); + else + virDomainObjUnlock(obj); } /** @@ -1130,7 +1157,8 @@ qemudWaitForMonitor(virConnectPtr conn, return -1; } - if (qemuConnectMonitor(vm, 0) < 0) + VIR_DEBUG("Connect monitor to %p '%s'", vm, vm->def->name); + if (qemuConnectMonitor(vm) < 0) return -1; return 0; @@ -2177,7 +2205,7 @@ static void qemudShutdownVMDaemon(virConnectPtr conn, if (!virDomainObjIsActive(vm)) return; - VIR_DEBUG(_("Shutting down VM '%s'\n"), vm->def->name); + VIR_DEBUG("Shutting down VM '%s'", vm->def->name); if (virKillProcess(vm->pid, 0) == 0 && virKillProcess(vm->pid, SIGTERM) < 0) @@ -6212,6 +6240,10 @@ qemudDomainMigratePrepareTunnel(virConnectPtr dconn, qemust = qemuStreamMigOpen(st, unixfile); if (qemust == NULL) { qemudShutdownVMDaemon(NULL, driver, vm); + if (!vm->persistent) { + virDomainRemoveInactive(&driver->domains, vm); + vm = NULL; + } virReportSystemError(dconn, errno, _("cannot open unix socket '%s' for tunnelled migration"), unixfile); diff --git a/src/qemu/qemu_monitor.c b/src/qemu/qemu_monitor.c index fd1c5e9..3e6a490 100644 --- a/src/qemu/qemu_monitor.c +++ b/src/qemu/qemu_monitor.c @@ -40,6 +40,9 @@ struct _qemuMonitor { virMutex lock; + virCond notify; + + virDomainObjPtr dom; int fd; int watch; @@ -49,6 +52,25 @@ struct _qemuMonitor { qemuMonitorEOFNotify eofCB; qemuMonitorDiskSecretLookup secretCB; + + /* If there's a command being processed this will be + * non-NULL */ + qemuMonitorMessagePtr msg; + + /* Buffer incoming data ready for Text/QMP monitor + * code to process & find message boundaries */ + size_t bufferOffset; + size_t bufferLength; + char *buffer; + + /* If anything went wrong, this will be fed back + * the next monitor msg */ + int lastErrno; + + /* If the monitor callback is currently active */ + unsigned eofcb: 1; + /* If the monitor callback should free the closed monitor */ + unsigned closed: 1; }; void qemuMonitorLock(qemuMonitorPtr mon) @@ -61,134 +83,25 @@ void qemuMonitorUnlock(qemuMonitorPtr mon) virMutexUnlock(&mon->lock); } -/* Return -1 for error, 1 to continue reading and 0 for success */ -typedef int qemuMonitorHandleOutput(virDomainObjPtr vm, - const char *output); - -/* - * Returns -1 for error, 0 on end-of-file, 1 for success - */ -static int -qemuMonitorReadOutput(virDomainObjPtr vm, - int fd, - char *buf, - size_t buflen, - qemuMonitorHandleOutput func, - const char *what, - int timeout) -{ - size_t got = 0; - buf[0] = '\0'; - timeout *= 1000; /* poll wants milli seconds */ - - /* Consume & discard the initial greeting */ - while (got < (buflen-1)) { - ssize_t ret; - - ret = read(fd, buf+got, buflen-got-1); - - if (ret < 0) { - struct pollfd pfd = { .fd = fd, .events = POLLIN }; - if (errno == EINTR) - continue; - - if (errno != EAGAIN) { - virReportSystemError(NULL, errno, - _("Failure while reading %s startup output"), - what); - return -1; - } - ret = poll(&pfd, 1, timeout); - if (ret == 0) { - qemudReportError(NULL, NULL, NULL, VIR_ERR_INTERNAL_ERROR, - _("Timed out while reading %s startup output"), what); - return -1; - } else if (ret == -1) { - if (errno != EINTR) { - virReportSystemError(NULL, errno, - _("Failure while reading %s startup output"), - what); - return -1; - } - } else { - /* Make sure we continue loop & read any further data - available before dealing with EOF */ - if (pfd.revents & (POLLIN | POLLHUP)) - continue; - - qemudReportError(NULL, NULL, NULL, VIR_ERR_INTERNAL_ERROR, - _("Failure while reading %s startup output"), what); - return -1; - } - } else if (ret == 0) { - return 0; - } else { - got += ret; - buf[got] = '\0'; - ret = func(vm, buf); - if (ret == -1) - return -1; - if (ret == 1) - continue; - return 1; - } - } - - qemudReportError(NULL, NULL, NULL, VIR_ERR_INTERNAL_ERROR, - _("Out of space while reading %s startup output"), what); - return -1; - -} - -static int -qemuMonitorCheckPrompt(virDomainObjPtr vm ATTRIBUTE_UNUSED, - const char *output) +static void qemuMonitorFree(qemuMonitorPtr mon, int lockDomain) { - if (strstr(output, "(qemu) ") == NULL) - return 1; /* keep reading */ - - return 0; -} - -static int -qemuMonitorOpenCommon(virDomainObjPtr vm, - int monfd, - int reconnect) -{ - char buf[1024]; - int ret; - - if (virSetCloseExec(monfd) < 0) { - qemudReportError(NULL, NULL, NULL, VIR_ERR_INTERNAL_ERROR, - "%s", _("Unable to set monitor close-on-exec flag")); - return -1; - } - if (virSetNonBlock(monfd) < 0) { - qemudReportError(NULL, NULL, NULL, VIR_ERR_INTERNAL_ERROR, - "%s", _("Unable to put monitor into non-blocking mode")); - return -1; - } - - if (!reconnect) { - if (qemuMonitorReadOutput(vm, monfd, - buf, sizeof(buf), - qemuMonitorCheckPrompt, - "monitor", 10) <= 0) - ret = -1; - else - ret = 0; - } else { - ret = 0; + VIR_DEBUG("mon=%p, lockDomain=%d", mon, lockDomain); + if (mon->vm) { + if (lockDomain) + virDomainObjLock(mon->vm); + if (!virDomainObjUnref(mon->vm) && lockDomain) + virDomainObjUnlock(mon->vm); } - - return ret; + if (virCondDestroy(&mon->notify) < 0) + {} + virMutexDestroy(&mon->lock); + VIR_FREE(mon); } + static int -qemuMonitorOpenUnix(virDomainObjPtr vm, - const char *monitor, - int reconnect) +qemuMonitorOpenUnix(const char *monitor) { struct sockaddr_un addr; int monfd; @@ -233,9 +146,6 @@ qemuMonitorOpenUnix(virDomainObjPtr vm, goto error; } - if (qemuMonitorOpenCommon(vm, monfd, reconnect) < 0) - goto error; - return monfd; error: @@ -244,9 +154,7 @@ error: } static int -qemuMonitorOpenPty(virDomainObjPtr vm, - const char *monitor, - int reconnect) +qemuMonitorOpenPty(const char *monitor) { int monfd; @@ -256,14 +164,181 @@ qemuMonitorOpenPty(virDomainObjPtr vm, return -1; } - if (qemuMonitorOpenCommon(vm, monfd, reconnect) < 0) - goto error; - return monfd; +} -error: - close(monfd); - return -1; + +static int +qemuMonitorIOProcess(qemuMonitorPtr mon) +{ + int len; + qemuMonitorMessagePtr msg = NULL; + + /* See if there's a message & whether its ready for its reply + * ie whether its completed writing all its data */ + if (mon->msg && mon->msg->txOffset == mon->msg->txLength) + msg = mon->msg; + + VIR_DEBUG("Process %d", mon->bufferOffset); + len = qemuMonitorTextIOProcess(mon, + mon->buffer, mon->bufferOffset, + msg); + + if (len < 0) { + mon->lastErrno = errno; + return -1; + } + + if (len < mon->bufferOffset) { + memmove(mon->buffer, mon->buffer + len, mon->bufferOffset - len); + mon->bufferOffset -= len; + } else { + VIR_FREE(mon->buffer); + mon->bufferOffset = mon->bufferLength = 0; + } + VIR_DEBUG("Process done %d used %d", mon->bufferOffset, len); + if (msg && msg->finished) + virCondBroadcast(&mon->notify); + return len; +} + + +static int +qemuMonitorIOWriteWithFD(qemuMonitorPtr mon, + const char *data, + size_t len, + int fd) +{ + struct msghdr msg; + struct iovec iov[1]; + int ret; + char control[CMSG_SPACE(sizeof(int))]; + struct cmsghdr *cmsg; + + if (!mon->hasSendFD) { + errno = EINVAL; + return -1; + } + + memset(&msg, 0, sizeof(msg)); + + iov[0].iov_base = (void *)data; + iov[0].iov_len = len; + + msg.msg_iov = iov; + msg.msg_iovlen = 1; + + msg.msg_control = control; + msg.msg_controllen = sizeof(control); + + cmsg = CMSG_FIRSTHDR(&msg); + cmsg->cmsg_len = CMSG_LEN(sizeof(int)); + cmsg->cmsg_level = SOL_SOCKET; + cmsg->cmsg_type = SCM_RIGHTS; + memcpy(CMSG_DATA(cmsg), &fd, sizeof(int)); + + do { + ret = sendmsg(mon->fd, &msg, 0); + } while (ret < 0 && errno == EINTR); + + return ret; +} + +/* Called when the monitor is able to write data */ +static int +qemuMonitorIOWrite(qemuMonitorPtr mon) +{ + int done; + + /* If no active message, or fully transmitted, the no-op */ + if (!mon->msg || mon->msg->txOffset == mon->msg->txLength) + return 0; + + if (mon->msg->txFD == -1) + done = write(mon->fd, + mon->msg->txBuffer + mon->msg->txOffset, + mon->msg->txLength - mon->msg->txOffset); + else + done = qemuMonitorIOWriteWithFD(mon, + mon->msg->txBuffer + mon->msg->txOffset, + mon->msg->txLength - mon->msg->txOffset, + mon->msg->txFD); + + if (done < 0) { + if (errno == EAGAIN) + return 0; + + mon->lastErrno = errno; + return -1; + } + mon->msg->txOffset += done; + return done; +} + +/* + * Called when the monitor has incoming data to read + * + * Returns -1 on error, or number of bytes read + */ +static int +qemuMonitorIORead(qemuMonitorPtr mon) +{ + size_t avail = mon->bufferLength - mon->bufferOffset; + int ret = 0; + + if (avail < 1024) { + if (VIR_REALLOC_N(mon->buffer, + mon->bufferLength + 1024) < 0) { + errno = ENOMEM; + return -1; + } + mon->bufferLength += 1024; + avail += 1024; + } + + /* Read as much as we can get into our buffer, + until we block on EAGAIN, or hit EOF */ + while (avail > 1) { + int got; + got = read(mon->fd, + mon->buffer + mon->bufferOffset, + avail - 1); + if (got < 0) { + if (errno == EAGAIN) + break; + mon->lastErrno = errno; + ret = -1; + break; + } + if (got == 0) + break; + + ret += got; + avail -= got; + mon->bufferOffset += got; + mon->buffer[mon->bufferOffset] = '\0'; + } + + VIR_DEBUG("Now read %d bytes of data", mon->bufferOffset); + + return ret; +} + + +static void qemuMonitorUpdateWatch(qemuMonitorPtr mon) +{ + int events = + VIR_EVENT_HANDLE_HANGUP | + VIR_EVENT_HANDLE_ERROR; + + if (!mon->lastErrno) { + events |= VIR_EVENT_HANDLE_READABLE; + + if (mon->msg && mon->msg->txOffset < mon->msg->txLength) + events |= VIR_EVENT_HANDLE_WRITABLE; + } + + virEventUpdateHandle(mon->watch, events); } @@ -272,29 +347,89 @@ qemuMonitorIO(int watch, int fd, int events, void *opaque) { qemuMonitorPtr mon = opaque; int quit = 0, failed = 0; + qemuMonitorLock(mon); + VIR_DEBUG("Monitor %p I/O on watch %d fd %d events %d", mon, watch, fd, events); + if (mon->fd != fd || mon->watch != watch) { - VIR_ERROR0(_("event from unexpected fd/watch")); + VIR_ERROR("event from unexpected fd %d!=%d / watch %d!=%d", mon->fd, fd, mon->watch, watch); failed = 1; } else { - if (events & (VIR_EVENT_HANDLE_HANGUP | VIR_EVENT_HANDLE_ERROR)) + if (!mon->lastErrno && + events & VIR_EVENT_HANDLE_WRITABLE) { + int done = qemuMonitorIOWrite(mon); + if (done < 0) + failed = 1; + events &= ~VIR_EVENT_HANDLE_WRITABLE; + } + if (!mon->lastErrno && + events & VIR_EVENT_HANDLE_READABLE) { + int got = qemuMonitorIORead(mon); + if (got < 0) + failed = 1; + /* Ignore hangup/error events if we read some data, to + * give time for that data to be consumed */ + if (got > 0) { + events = 0; + + if (qemuMonitorIOProcess(mon) < 0) + failed = 1; + } else + events &= ~VIR_EVENT_HANDLE_READABLE; + } + + /* If IO process resulted in an error & we have a message, + * then wakeup that waiter */ + if (mon->lastErrno && mon->msg && !mon->msg->finished) { + mon->msg->lastErrno = mon->lastErrno; + mon->msg->finished = 1; + virCondSignal(&mon->notify); + } + + qemuMonitorUpdateWatch(mon); + + if (events & VIR_EVENT_HANDLE_HANGUP) { + /* If IO process resulted in EOF & we have a message, + * then wakeup that waiter */ + if (mon->msg && !mon->msg->finished) { + mon->msg->finished = 1; + mon->msg->lastErrno = EIO; + virCondSignal(&mon->notify); + } quit = 1; - else { + } else if (events) { VIR_ERROR(_("unhandled fd event %d for monitor fd %d"), events, mon->fd); failed = 1; } } - mon->eofCB(mon, mon->vm, failed); + /* We have to unlock to avoid deadlock against command thread, + * but is this safe ? I think it is, because the callback + * will try to acquire the virDomainObjPtr mutex next */ + if (failed || quit) { + /* Make sure anyone waiting wakes up now */ + virCondSignal(&mon->notify); + mon->eofcb = 1; + qemuMonitorUnlock(mon); + VIR_DEBUG("Triggering EOF callback error? %d", failed); + mon->eofCB(mon, mon->vm, failed); + + qemuMonitorLock(mon); + if (mon->closed) { + qemuMonitorUnlock(mon); + VIR_DEBUG("Delayed free of monitor %p", mon); + qemuMonitorFree(mon, 1); + } else { + qemuMonitorUnlock(mon); + } + } else { + qemuMonitorUnlock(mon); + } } - - - qemuMonitorPtr qemuMonitorOpen(virDomainObjPtr vm, - int reconnect, qemuMonitorEOFNotify eofCB) { qemuMonitorPtr mon; @@ -310,20 +445,26 @@ qemuMonitorOpen(virDomainObjPtr vm, VIR_FREE(mon); return NULL; } + if (virCondInit(&mon->notify) < 0) { + qemudReportError(NULL, NULL, NULL, VIR_ERR_INTERNAL_ERROR, "%s", + _("cannot initialize monitor condition")); + virMutexDestroy(&mon->lock); + VIR_FREE(mon); + return NULL; + } mon->fd = -1; mon->vm = vm; mon->eofCB = eofCB; + qemuMonitorLock(mon); switch (vm->monitor_chr->type) { case VIR_DOMAIN_CHR_TYPE_UNIX: mon->hasSendFD = 1; - mon->fd = qemuMonitorOpenUnix(vm, vm->monitor_chr->data.nix.path, - reconnect); + mon->fd = qemuMonitorOpenUnix(vm->monitor_chr->data.nix.path); break; case VIR_DOMAIN_CHR_TYPE_PTY: - mon->fd = qemuMonitorOpenPty(vm, vm->monitor_chr->data.file.path, - reconnect); + mon->fd = qemuMonitorOpenPty(vm->monitor_chr->data.file.path); break; default: @@ -333,8 +474,22 @@ qemuMonitorOpen(virDomainObjPtr vm, goto cleanup; } + if (virSetCloseExec(mon->fd) < 0) { + qemudReportError(NULL, NULL, NULL, VIR_ERR_INTERNAL_ERROR, + "%s", _("Unable to set monitor close-on-exec flag")); + goto cleanup; + } + if (virSetNonBlock(mon->fd) < 0) { + qemudReportError(NULL, NULL, NULL, VIR_ERR_INTERNAL_ERROR, + "%s", _("Unable to put monitor into non-blocking mode")); + goto cleanup; + } + + if ((mon->watch = virEventAddHandle(mon->fd, - VIR_EVENT_HANDLE_HANGUP | VIR_EVENT_HANDLE_ERROR, + VIR_EVENT_HANDLE_HANGUP | + VIR_EVENT_HANDLE_ERROR | + VIR_EVENT_HANDLE_READABLE, qemuMonitorIO, mon, NULL)) < 0) { qemudReportError(NULL, NULL, NULL, VIR_ERR_INTERNAL_ERROR, "%s", @@ -342,10 +497,15 @@ qemuMonitorOpen(virDomainObjPtr vm, goto cleanup; } + virDomainObjRef(vm); + + VIR_DEBUG("New mon %p fd =%d watch=%d", mon, mon->fd, mon->watch); + qemuMonitorUnlock(mon); return mon; cleanup: + qemuMonitorUnlock(mon); qemuMonitorClose(mon); return NULL; } @@ -356,13 +516,24 @@ void qemuMonitorClose(qemuMonitorPtr mon) if (!mon) return; - if (mon->watch) - virEventRemoveHandle(mon->watch); + qemuMonitorLock(mon); + if (!mon->closed) { + if (mon->watch) + virEventRemoveHandle(mon->watch); + if (mon->fd != -1) + close(mon->fd); + /* NB: don't reset fd / watch fields, since active + * callback may still want them */ + mon->closed = 1; + } - if (mon->fd != -1) - close(mon->fd); - virMutexDestroy(&mon->lock); - VIR_FREE(mon); + if (mon->eofcb) { + VIR_DEBUG("Mark monitor to be deleted %p", mon); + qemuMonitorUnlock(mon); + } else { + VIR_DEBUG("Delete monitor now %p", mon); + qemuMonitorFree(mon, 0); + } } @@ -373,71 +544,33 @@ void qemuMonitorRegisterDiskSecretLookup(qemuMonitorPtr mon, } -int qemuMonitorWrite(qemuMonitorPtr mon, - const char *data, - size_t len) +int qemuMonitorSend(qemuMonitorPtr mon, + qemuMonitorMessagePtr msg) { - return safewrite(mon->fd, data, len); -} - -int qemuMonitorWriteWithFD(qemuMonitorPtr mon, - const char *data, - size_t len, - int fd) -{ - struct msghdr msg; - struct iovec iov[1]; - ssize_t ret; - char control[CMSG_SPACE(sizeof(int))]; - struct cmsghdr *cmsg; + int ret = -1; - if (!mon->hasSendFD) { - errno = EINVAL; + if (mon->eofcb) { + msg->lastErrno = EIO; + qemuMonitorUnlock(mon); return -1; } - memset(&msg, 0, sizeof(msg)); - - iov[0].iov_base = (void *)data; - iov[0].iov_len = len; - - msg.msg_iov = iov; - msg.msg_iovlen = 1; - - msg.msg_control = control; - msg.msg_controllen = sizeof(control); - - cmsg = CMSG_FIRSTHDR(&msg); - cmsg->cmsg_len = CMSG_LEN(sizeof(int)); - cmsg->cmsg_level = SOL_SOCKET; - cmsg->cmsg_type = SCM_RIGHTS; - memcpy(CMSG_DATA(cmsg), &fd, sizeof(int)); - - do { - ret = sendmsg(mon->fd, &msg, 0); - } while (ret < 0 && errno == EINTR); + mon->msg = msg; + qemuMonitorUpdateWatch(mon); - return ret == len ? 0 : -1; -} + while (!mon->msg->finished) { + if (virCondWait(&mon->notify, &mon->lock) < 0) + goto cleanup; + } -int qemuMonitorRead(qemuMonitorPtr mon, - char *data, - size_t len) -{ - return read(mon->fd, data, len); -} + if (mon->lastErrno == 0) + ret = 0; -int qemuMonitorWaitForInput(qemuMonitorPtr mon) -{ - struct pollfd fd = { mon->fd, POLLIN | POLLERR | POLLHUP, 0 }; +cleanup: + mon->msg = NULL; + qemuMonitorUpdateWatch(mon); -retry: - if (poll(&fd, 1, -1) < 0) { - if (errno == EINTR) - goto retry; - return -1; - } - return 0; + return ret; } diff --git a/src/qemu/qemu_monitor.h b/src/qemu/qemu_monitor.h index a8d517c..71688cb 100644 --- a/src/qemu/qemu_monitor.h +++ b/src/qemu/qemu_monitor.h @@ -32,6 +32,33 @@ typedef struct _qemuMonitor qemuMonitor; typedef qemuMonitor *qemuMonitorPtr; +typedef struct _qemuMonitorMessage qemuMonitorMessage; +typedef qemuMonitorMessage *qemuMonitorMessagePtr; + +typedef int (*qemuMonitorPasswordHandler)(qemuMonitorPtr mon, + qemuMonitorMessagePtr msg, + const char *data, + size_t len, + void *opaque); + +struct _qemuMonitorMessage { + int txFD; + + char *txBuffer; + int txOffset; + int txLength; + + char *rxBuffer; + int rxLength; + + int finished; + + int lastErrno; + + qemuMonitorPasswordHandler passwordHandler; + void *passwordOpaque; +}; + typedef void (*qemuMonitorEOFNotify)(qemuMonitorPtr mon, virDomainObjPtr vm, int withError); @@ -49,7 +76,6 @@ typedef int (*qemuMonitorDiskSecretLookup)(qemuMonitorPtr mon, size_t *secretLen); qemuMonitorPtr qemuMonitorOpen(virDomainObjPtr vm, - int reconnect, qemuMonitorEOFNotify eofCB); void qemuMonitorClose(qemuMonitorPtr mon); @@ -60,21 +86,11 @@ void qemuMonitorUnlock(qemuMonitorPtr mon); void qemuMonitorRegisterDiskSecretLookup(qemuMonitorPtr mon, qemuMonitorDiskSecretLookup secretCB); -int qemuMonitorWrite(qemuMonitorPtr mon, - const char *data, - size_t len); - -int qemuMonitorWriteWithFD(qemuMonitorPtr mon, - const char *data, - size_t len, - int fd); - -int qemuMonitorRead(qemuMonitorPtr mon, - char *data, - size_t len); - -int qemuMonitorWaitForInput(qemuMonitorPtr mon); +/* This API is for use by the internal Text/JSON monitor impl code only */ +int qemuMonitorSend(qemuMonitorPtr mon, + qemuMonitorMessagePtr msg); +/* XXX same comment about virConnectPtr as above */ int qemuMonitorGetDiskSecret(qemuMonitorPtr mon, virConnectPtr conn, const char *path, diff --git a/src/qemu/qemu_monitor_text.c b/src/qemu/qemu_monitor_text.c index 7ad7f09..db7ff57 100644 --- a/src/qemu/qemu_monitor_text.c +++ b/src/qemu/qemu_monitor_text.c @@ -133,181 +133,163 @@ static char *qemuMonitorEscapeShell(const char *in) return qemuMonitorEscape(in, 1); } -/* Throw away any data available on the monitor - * This is done before executing a command, in order - * to allow re-synchronization if something went badly - * wrong in the past. it also deals with problem of - * QEMU *sometimes* re-printing its initial greeting - * when we reconnect to the monitor after restarts. +/* When connecting to a monitor, QEMU will print a greeting like + * + * QEMU 0.11.0 monitor - type 'help' for more information + * + * Don't expect the version number bit to be stable :-) */ -static void -qemuMonitorDiscardPendingData(qemuMonitorPtr mon) { - char buf[1024]; - int ret = 0; - - /* Monitor is non-blocking, so just loop till we - * get -1 or 0. Don't bother with detecting - * errors, since we'll deal with that better later */ - do { - ret = qemuMonitorRead(mon, buf, sizeof (buf)-1); - } while (ret > 0); -} +#define GREETING_PREFIX "QEMU " +#define GREETING_POSTFIX "type 'help' for more information\r\n(qemu) " +#define BASIC_PROMPT "(qemu) " +#define PASSWORD_PROMPT "Password:" +#define DISK_ENCRYPTION_PREFIX "(" +#define DISK_ENCRYPTION_POSTFIX ") is encrypted." +#define LINE_ENDING "\r\n" + +int qemuMonitorTextIOProcess(qemuMonitorPtr mon ATTRIBUTE_UNUSED, + const char *data, + size_t len, + qemuMonitorMessagePtr msg) +{ + int used = 0; + /* Check for & discard greeting */ + if (STRPREFIX(data, GREETING_PREFIX)) { + const char *offset = strstr(data, GREETING_POSTFIX); -static int -qemuMonitorSend(qemuMonitorPtr mon, - const char *cmd, - int scm_fd) -{ - char *full; - size_t len; - int ret = -1; + /* We see the greeting prefix, but not postfix, so pretend we've + not consumed anything. We'll restart when more data arrives. */ + if (!offset) { + VIR_DEBUG0("Partial greeting seen, getting out & waiting for more"); + return 0; + } - if (virAsprintf(&full, "%s\r", cmd) < 0) - return -1; + used = offset - data + strlen(GREETING_POSTFIX); - len = strlen(full); + VIR_DEBUG0("Discarded monitor greeting"); + } - if (scm_fd == -1) - ret = qemuMonitorWrite(mon, full, len); - else - ret = qemuMonitorWriteWithFD(mon, full, len, scm_fd); + /* Don't print raw data in debug because its full of control chars */ + /*VIR_DEBUG("Process data %d byts of data [%s]", len - used, data + used);*/ + VIR_DEBUG("Process data %d byts of data", len - used); - VIR_FREE(full); - return ret; + /* Look for a non-zero reply followed by prompt */ + if (msg && !msg->finished) { + const char *end; + + /* We might get a prompt for a password */ + end = strstr(data + used, PASSWORD_PROMPT); + if (end) { + VIR_DEBUG("Woooo passwowrd [%s]", data + used); + if (msg->passwordHandler) { + size_t consumed; + /* Try and handle the prompt */ + if (msg->passwordHandler(mon, msg, + data + used, + len - used, + msg->passwordOpaque) < 0) + return -1; + + /* Skip over prompt now */ + consumed = (end + strlen(PASSWORD_PROMPT)) + - (data + used); + used += consumed; + } else { + errno = EACCES; + return -1; + } + } + + /* We use the arrival of BASIC_PROMPT to detect when we've got a + * complete reply available from a command */ + end = strstr(data + used, BASIC_PROMPT); + if (end) { + /* QEMU echos the command back to us, full of control + * character junk that we don't want. Fortunately this + * is all terminated by LINE_ENDING, so we can easily + * skip over the control character junk */ + const char *start = strstr(data + used, LINE_ENDING); + if (!start) + start = data + used; + else + start += strlen(LINE_ENDING); + int want = end - start; + + /* Annoyingly some commands may not have any reply data + * at all upon success, but since we've detected the + * BASIC_PROMPT we can reasonably reliably cope */ + if (want) { + if (VIR_REALLOC_N(msg->rxBuffer, + msg->rxLength + want + 1) < 0) + return -1; + memcpy(msg->rxBuffer + msg->rxLength, start, want); + msg->rxLength += want; + msg->rxBuffer[msg->rxLength] = '\0'; + VIR_DEBUG("Finished %d byte reply [%s]", want, msg->rxBuffer); + } else { + VIR_DEBUG0("Finished 0 byte reply"); + } + msg->finished = 1; + used += end - (data + used); + used += strlen(BASIC_PROMPT); + } + } + + VIR_DEBUG("Total used %d", used); + return used; } static int qemuMonitorCommandWithHandler(qemuMonitorPtr mon, const char *cmd, - const char *extraPrompt, - qemuMonitorExtraPromptHandler extraHandler, - void *handlerData, + qemuMonitorPasswordHandler passwordHandler, + void *passwordOpaque, int scm_fd, char **reply) { - int size = 0; - char *buf = NULL; + int ret; + qemuMonitorMessage msg; - qemuMonitorDiscardPendingData(mon); + *reply = NULL; - VIR_DEBUG("cmd='%s' extraPrompt='%s'", cmd, NULLSTR(extraPrompt)); - if (qemuMonitorSend(mon, cmd, scm_fd) < 0) + memset(&msg, 0, sizeof msg); + + if (virAsprintf(&msg.txBuffer, "%s\r", cmd) < 0) { + virReportOOMError(NULL); return -1; + } + msg.txLength = strlen(msg.txBuffer); + msg.txFD = scm_fd; + msg.passwordHandler = passwordHandler; + msg.passwordOpaque = passwordOpaque; - *reply = NULL; + VIR_DEBUG("Send command '%s' for write with FD %d", cmd, scm_fd); - for (;;) { - /* Read all the data QEMU has sent thus far */ - for (;;) { - char data[1024]; - int got = qemuMonitorRead(mon, data, sizeof(data)); - - if (got == 0) - goto error; - if (got < 0) { - if (errno == EINTR) - continue; - if (errno == EAGAIN) - break; - goto error; - } - if (VIR_REALLOC_N(buf, size+got+1) < 0) - goto error; + ret = qemuMonitorSend(mon, &msg); - memmove(buf+size, data, got); - buf[size+got] = '\0'; - size += got; - } - - /* Look for QEMU prompt to indicate completion */ - if (buf) { - char *foundPrompt; - char *tmp; + VIR_DEBUG("Receive command reply ret=%d errno=%d %d bytes '%s'", + ret, msg.lastErrno, msg.rxLength, msg.rxBuffer); - if (extraPrompt && - (foundPrompt = strstr(buf, extraPrompt)) != NULL) { - char *promptEnd; + /* Just in case buffer had some passwords in */ + memset(msg.txBuffer, 0, msg.txLength); + VIR_FREE(msg.txBuffer); - DEBUG("prompt='%s' handler=%p", extraPrompt, extraHandler); - if (extraHandler(mon, buf, foundPrompt, handlerData) < 0) - return -1; - /* Discard output so far, necessary to detect whether - extraPrompt appears again. We don't need the output between - original command and this prompt anyway. */ - promptEnd = foundPrompt + strlen(extraPrompt); - memmove(buf, promptEnd, strlen(promptEnd)+1); - size -= promptEnd - buf; - } else if ((tmp = strstr(buf, QEMU_CMD_PROMPT)) != NULL) { - char *commptr = NULL, *nlptr = NULL; - /* Preserve the newline */ - tmp[1] = '\0'; - - /* The monitor doesn't dump clean output after we have written to - * it. Every character we write dumps a bunch of useless stuff, - * so the result looks like "cXcoXcomXcommXcommaXcommanXcommand" - * Try to throw away everything before the first full command - * occurence, and inbetween the command and the newline starting - * the response - */ - if ((commptr = strstr(buf, cmd))) { - memmove(buf, commptr, strlen(commptr)+1); - if ((nlptr = strchr(buf, '\n'))) - memmove(buf+strlen(cmd), nlptr, strlen(nlptr)+1); - } - - break; - } + /* To make life safer for callers, already ensure there's at least an empty string */ + if (msg.rxBuffer) { + *reply = msg.rxBuffer; + } else { + *reply = strdup(""); + if (!*reply) { + virReportOOMError(NULL); + return -1; } - - /* Need to wait for more data */ - if (qemuMonitorWaitForInput(mon) < 0) - goto error; } - *reply = buf; - DEBUG("reply='%s'", buf); - return 0; - error: - VIR_FREE(buf); - return -1; -} + if (ret < 0) + virReportSystemError(NULL, msg.lastErrno, + _("cannot send monitor command '%s'"), cmd); -struct extraHandlerData -{ - const char *reply; - bool first; -}; - -static int -qemuMonitorCommandSimpleExtraHandler(qemuMonitorPtr mon, - const char *buf ATTRIBUTE_UNUSED, - const char *prompt ATTRIBUTE_UNUSED, - void *data_) -{ - struct extraHandlerData *data = data_; - - if (!data->first) - return 0; - if (qemuMonitorSend(mon, data->reply, -1) < 0) - return -1; - data->first = false; - return 0; -} - -static int -qemuMonitorCommandExtra(qemuMonitorPtr mon, - const char *cmd, - const char *extra, - const char *extraPrompt, - int scm_fd, - char **reply) { - struct extraHandlerData data; - - data.reply = extra; - data.first = true; - return qemuMonitorCommandWithHandler(mon, cmd, extraPrompt, - qemuMonitorCommandSimpleExtraHandler, - &data, scm_fd, reply); + return ret; } static int @@ -315,7 +297,7 @@ qemuMonitorCommandWithFd(qemuMonitorPtr mon, const char *cmd, int scm_fd, char **reply) { - return qemuMonitorCommandExtra(mon, cmd, NULL, NULL, scm_fd, reply); + return qemuMonitorCommandWithHandler(mon, cmd, NULL, NULL, scm_fd, reply); } static int @@ -327,44 +309,74 @@ qemuMonitorCommand(qemuMonitorPtr mon, static int -qemuMonitorSendVolumePassphrase(qemuMonitorPtr mon, - const char *buf, - const char *prompt, - void *data) +qemuMonitorSendDiskPassphrase(qemuMonitorPtr mon, + qemuMonitorMessagePtr msg, + const char *data, + size_t len ATTRIBUTE_UNUSED, + void *opaque) { - virConnectPtr conn = data; - char *passphrase = NULL, *path; - const char *prompt_path; - size_t path_len, passphrase_len = 0; + virConnectPtr conn = opaque; + char *path; + char *passphrase = NULL; + size_t passphrase_len = 0; int res; + const char *pathStart; + const char *pathEnd; - /* The complete prompt looks like this: - ide0-hd0 (/path/to/volume) is encrypted. - Password: - "prompt" starts with ") is encrypted". Extract /path/to/volume. */ - for (prompt_path = prompt; prompt_path > buf && prompt_path[-1] != '('; - prompt_path--) - ; - if (prompt_path == buf) + /* + * For disk passwords: + * + * ide0-hd0 (/path/to/volume) is encrypted. + * Password: + * + */ + pathStart = strstr(data, DISK_ENCRYPTION_PREFIX); + pathEnd = strstr(data, DISK_ENCRYPTION_POSTFIX); + if (!pathStart || !pathEnd || pathStart >= pathEnd) { + errno = -EINVAL; return -1; - path_len = prompt - prompt_path; - if (VIR_ALLOC_N(path, path_len + 1) < 0) + } + + /* Extra the path */ + pathStart += strlen(DISK_ENCRYPTION_PREFIX); + path = strndup(pathStart, pathEnd - pathStart); + if (!path) { + errno = ENOMEM; return -1; - memcpy(path, prompt_path, path_len); - path[path_len] = '\0'; + } - res = qemuMonitorGetDiskSecret(mon, conn, path, - &passphrase, &passphrase_len); + /* Fetch the disk password if possible */ + res = qemuMonitorGetDiskSecret(mon, + conn, + path, + &passphrase, + &passphrase_len); VIR_FREE(path); if (res < 0) return -1; - res = qemuMonitorSend(mon, passphrase, -1); + /* Enlarge transmit buffer to allow for the extra data + * to be sent back */ + if (VIR_REALLOC_N(msg->txBuffer, + msg->txLength + passphrase_len + 1 + 1) < 0) { + memset(passphrase, 0, passphrase_len); + VIR_FREE(passphrase); + errno = ENOMEM; + return -1; + } + + /* Queue the password for sending */ + memcpy(msg->txBuffer + msg->txLength, + passphrase, passphrase_len); + msg->txLength += passphrase_len; + msg->txBuffer[msg->txLength] = '\r'; + msg->txLength++; + msg->txBuffer[msg->txLength] = '\0'; memset(passphrase, 0, passphrase_len); VIR_FREE(passphrase); - return res; + return 0; } int @@ -372,8 +384,9 @@ qemuMonitorTextStartCPUs(qemuMonitorPtr mon, virConnectPtr conn) { char *reply; - if (qemuMonitorCommandWithHandler(mon, "cont", ") is encrypted.", - qemuMonitorSendVolumePassphrase, conn, + if (qemuMonitorCommandWithHandler(mon, "cont", + qemuMonitorSendDiskPassphrase, + conn, -1, &reply) < 0) return -1; @@ -637,15 +650,44 @@ int qemuMonitorTextGetBlockStatsInfo(qemuMonitorPtr mon, } +static int +qemuMonitorSendVNCPassphrase(qemuMonitorPtr mon ATTRIBUTE_UNUSED, + qemuMonitorMessagePtr msg, + const char *data ATTRIBUTE_UNUSED, + size_t len ATTRIBUTE_UNUSED, + void *opaque) +{ + char *passphrase = opaque; + size_t passphrase_len = strlen(passphrase); + + /* Enlarge transmit buffer to allow for the extra data + * to be sent back */ + if (VIR_REALLOC_N(msg->txBuffer, + msg->txLength + passphrase_len + 1 + 1) < 0) { + errno = ENOMEM; + return -1; + } + + /* Queue the password for sending */ + memcpy(msg->txBuffer + msg->txLength, + passphrase, passphrase_len); + msg->txLength += passphrase_len; + msg->txBuffer[msg->txLength] = '\r'; + msg->txLength++; + msg->txBuffer[msg->txLength] = '\0'; + + return 0; +} + int qemuMonitorTextSetVNCPassword(qemuMonitorPtr mon, const char *password) { char *info = NULL; - if (qemuMonitorCommandExtra(mon, "change vnc password", - password, - QEMU_PASSWD_PROMPT, - -1, &info) < 0) { + if (qemuMonitorCommandWithHandler(mon, "change vnc password", + qemuMonitorSendVNCPassphrase, + (char *)password, + -1, &info) < 0) { qemudReportError(NULL, NULL, NULL, VIR_ERR_INTERNAL_ERROR, "%s", _("setting VNC password failed")); return -1; diff --git a/src/qemu/qemu_monitor_text.h b/src/qemu/qemu_monitor_text.h index 35eaf05..6bca07a 100644 --- a/src/qemu/qemu_monitor_text.h +++ b/src/qemu/qemu_monitor_text.h @@ -29,6 +29,11 @@ #include "qemu_monitor.h" +int qemuMonitorTextIOProcess(qemuMonitorPtr mon, + const char *data, + size_t len, + qemuMonitorMessagePtr msg); + int qemuMonitorTextStartCPUs(qemuMonitorPtr mon, virConnectPtr conn); int qemuMonitorTextStopCPUs(qemuMonitorPtr mon); -- 1.6.2.5

On Tue, Nov 03, 2009 at 02:50:04PM -0500, Daniel P. Berrange wrote:
Change the QEMU monitor file handle watch to poll for both read & write events, as well as EOF. All I/O to/from the QEMU monitor FD is now done in the event callback thread.
When the QEMU driver needs to send a command, it puts the data to be sent into a qemuMonitorMessagePtr object instance, queues it for dispatch, and then goes to sleep on a condition variable. The event thread sends all the data, and then waits for the reply to arrive, putting the response / error data back into the qemuMonitorMessagePtr and notifying the condition variable.
* src/qemu/qemu_driver.c: XXX this change shouldn't be here * src/qemu/qemu_monitor.c, src/qemu/qemu_monitor.h: Remove raw I/O functions, and a generic qemuMonitorSend() for invoking a command * src/qemu/qemu_monitor_text.c, src/qemu/qemu_monitor_text.h: Remove all low level I/O, and use the new qemuMonitorSend() API. Provide a qemuMonitorTextIOProcess() method for detecting command/reply/prompt boundaries in the monitor data stream
I tried to review it, but honnestly I can't keep up, it's getting too complex. But I don't want to block, ACK Daniel -- Daniel Veillard | libxml Gnome XML XSLT toolkit http://xmlsoft.org/ daniel@veillard.com | Rpmfind RPM search engine http://rpmfind.net/ http://veillard.com/ | virtualization library http://libvirt.org/

QEMU monitor commands may sleep for a prolonged period of time. If the virDomainObjPtr or qemu driver lock is held this will needlessly block execution of many other API calls. it also prevents asynchronous monitor events from being dispatched while a monitor command is executing, because deadlock will ensure. To resolve this, it is neccessary to release all locks while executing a monitor command. This change introduces a flag indicating that a monitor job is active, and a condition variable to synchronize access to this flag. This ensures that only a single thread can be making a state change or executing a monitor command at a time, while still allowing other API calls to be completed without blocking * src/qemu/qemu_driver.c: Release driver and domain lock when running monitor commands * src/qemu/THREADS.txt: Document threading rules --- src/qemu/THREADS.txt | 283 ++++++++++++++++++++++ src/qemu/qemu_driver.c | 616 +++++++++++++++++++++++++++++++++++------------ 2 files changed, 741 insertions(+), 158 deletions(-) create mode 100644 src/qemu/THREADS.txt diff --git a/src/qemu/THREADS.txt b/src/qemu/THREADS.txt new file mode 100644 index 0000000..1af1b83 --- /dev/null +++ b/src/qemu/THREADS.txt @@ -0,0 +1,283 @@ + QEMU Driver Threading: The Rules + ================================= + +This document describes how thread safety is ensured throughout +the QEMU driver. The criteria for this model are: + + - Objects must never be exclusively locked for any pro-longed time + - Code which sleeps must be able to time out after suitable period + - Must be safe against dispatch asynchronous events from monitor + + +Basic locking primitives +------------------------ + +There are a number of locks on various objects + + * struct qemud_driver: RWLock + + This is the top level lock on the entire driver. Every API call in + the QEMU driver is blocked while this is held, though some internal + callbacks may still run asynchronously. This lock must never be held + for anything which sleeps/waits (ie monitor commands) + + When obtaining the driver lock, under *NO* circumstances must + any lock be held on a virDomainObjPtr. This *WILL* result in + deadlock. + + + + * virDomainObjPtr: Mutex + + Will be locked after calling any of the virDomainFindBy{ID,Name,UUID} + methods. + + Lock must be held when changing/reading any variable in the virDomainObjPtr + + Once the lock is held, you must *NOT* try to lock the driver. You must + release all virDomainObjPtr locks before locking the driver, or deadlock + *WILL* occurr. + + If the lock needs to be dropped & then re-acquired for a short period of + time, the reference count must be incremented first using virDomainObjRef(). + If the reference count is incremented in this way, it is not neccessary + to have the driver locked when re-acquiring the dropped locked, since the + reference count prevents it being freed by another thread. + + This lock must not be held for anything which sleeps/waits (ie monitor + commands). + + + + * qemuMonitorPrivatePtr: Job condition + + Since virDomainObjPtr lock must not be held during sleeps, the job condition + provides additional protection for code making updates. + + Immediately after acquiring the virDomainObjPtr lock, any method which intends + to update state, must acquire the job condition. The virDomainObjPtr lock + is released while blocking on this condition variable. Once the job condition + is acquired a method can safely release the virDomainObjPtr lock whenever it + hits a piece of code which may sleep/wait, and re-acquire it after the sleep/ + wait. + + + * qemuMonitorPtr: Mutex + + Lock to be used when invoking any monitor command to ensure safety + wrt any asynchronous events that may be dispatched from the monitor. + It should be acquired before running a command. + + The job condition *MUST* be held before acquiring the monitor lock + + The virDomainObjPtr lock *MUST* be held before acquiring the monitor + lock. + + The virDomainObjPtr lock *MUST* then be released when invoking the + monitor command. + + The driver lock *MUST* be released when invoking the monitor commands. + + This ensures that the virDomainObjPtr & driver are both unlocked while + sleeping/waiting for the monitor response. + + + +Helper methods +-------------- + +To lock the driver + + qemuDriverLock() + - Acquires the driver lock + + qemuDriverUnlock() + - Releases the driver lock + + + +To lock the virDomainObjPtr + + virDomainObjLock() + - Acquires the virDomainObjPtr lock + + virDomainObjUnlock() + - Releases the virDomainObjPtr lock + + + +To acquire the job mutex + + qemuDomainObjBeginJob() (if driver is unlocked) + - Increments ref count on virDomainObjPtr + - Wait qemuDomainObjPrivate condition 'jobActive != 0' using virDomainObjPtr mutex + - Sets jobActive to 1 + + qemuDomainObjBeginJobWithDriver() (if driver needs to be locked) + - Unlocks driver + - Increments ref count on virDomainObjPtr + - Wait qemuDomainObjPrivate condition 'jobActive != 0' using virDomainObjPtr mutex + - Sets jobActive to 1 + - Unlocks virDomainObjPtr + - Locks driver + - Locks virDomainObjPtr + + NB: this variant is required in order to comply with lock ordering rules + for virDomainObjPtr vs driver + + + qemuDomainObjEndJob() + - Set jobActive to 0 + - Signal on qemuDomainObjPrivate condition + - Decrements ref count on virDomainObjPtr + + + +To acquire the QEMU monitor lock + + qemuDomainObjEnterMonitor() + - Acquires the qemuMonitorObjPtr lock + - Releases the virDomainObjPtr lock + + qemuDomainObjExitMonitor() + - Acquires the virDomainObjPtr lock + - Releases the qemuMonitorObjPtr lock + + NB: caller must take care to drop the driver lock if neccessary + + +To acquire the QEMU monitor lock with the driver lock held + + qemuDomainObjEnterMonitorWithDriver() + - Acquires the qemuMonitorObjPtr lock + - Releases the virDomainObjPtr lock + - Releases the driver lock + + qemuDomainObjExitMonitorWithDriver() + - Acquires the driver lock + - Acquires the virDomainObjPtr lock + - Releases the qemuMonitorObjPtr lock + + NB: caller must take care to drop the driver lock if neccessary + + +Design patterns +--------------- + + + * Accessing or updating something with just the driver + + qemuDriverLock(driver); + + ...do work... + + qemuDriverUnlock(driver); + + + + * Accessing something directly todo with a virDomainObjPtr + + virDomainObjPtr obj; + + qemuDriverLock(driver); + obj = virDomainFindByUUID(driver->domains, dom->uuid); + qemuDriverUnlock(driver); + + ...do work... + + virDomainObjUnlock(obj); + + + + * Accessing something directly todo with a virDomainObjPtr and driver + + virDomainObjPtr obj; + + qemuDriverLock(driver); + obj = virDomainFindByUUID(driver->domains, dom->uuid); + + ...do work... + + virDomainObjUnlock(obj); + qemuDriverUnlock(driver); + + + + * Updating something directly todo with a virDomainObjPtr + + virDomainObjPtr obj; + + qemuDriverLockRO(driver); + obj = virDomainFindByUUID(driver->domains, dom->uuid); + qemuDriverUnlock(driver); + + qemuDomainObjBeginJob(obj); + + ...do work... + + qemuDomainObjEndJob(obj); + + virDomainObjUnlock(obj); + + + + + * Invoking a monitor command on a virDomainObjPtr + + + virDomainObjPtr obj; + qemuDomainObjPrivatePtr priv; + + qemuDriverLockRO(driver); + obj = virDomainFindByUUID(driver->domains, dom->uuid); + qemuDriverUnlock(driver); + + qemuDomainObjBeginJob(obj); + + ...do prep work... + + qemuDomainObjEnterMonitor(obj); + qemuMonitorXXXX(priv->mon); + qemuDomainObjExitMonitor(obj); + + ...do final work... + + qemuDomainObjEndJob(obj); + virDomainObjUnlock(obj); + + + + + * Invoking a monitor command on a virDomainObjPtr with driver locked too + + + virDomainObjPtr obj; + qemuDomainObjPrivatePtr priv; + + qemuDriverLock(driver); + obj = virDomainFindByUUID(driver->domains, dom->uuid); + + qemuDomainObjBeginJobWithDriver(obj); + + ...do prep work... + + qemuDomainObjEnterMonitorWithDriver(driver, obj); + qemuMonitorXXXX(priv->mon); + qemuDomainObjExitMonitorWithDriver(driver, obj); + + ...do final work... + + qemuDomainObjEndJob(obj); + virDomainObjUnlock(obj); + qemuDriverUnlock(driver); + + + +Summary +------- + + * Respect lock ordering rules: never lock driver if anything else is + already locked + + * Don't hold locks in code which sleeps: unlock driver & virDomainObjPtr + when using monitor diff --git a/src/qemu/qemu_driver.c b/src/qemu/qemu_driver.c index fea439b..b7cde56 100644 --- a/src/qemu/qemu_driver.c +++ b/src/qemu/qemu_driver.c @@ -25,6 +25,7 @@ #include <sys/types.h> #include <sys/poll.h> +#include <sys/time.h> #include <dirent.h> #include <limits.h> #include <string.h> @@ -79,6 +80,11 @@ typedef struct _qemuDomainObjPrivate qemuDomainObjPrivate; typedef qemuDomainObjPrivate *qemuDomainObjPrivatePtr; struct _qemuDomainObjPrivate { + virCond jobCond; /* Use in conjunction with main virDomainObjPtr lock */ + int jobActive; /* Non-zero if a job is active. Only 1 job is allowed at any time + * A job includes *all* monitor commands, even those just querying + * information, not merely actions */ + qemuMonitorPtr mon; }; @@ -141,19 +147,145 @@ static void qemuDomainObjPrivateFree(void *data) } +/* + * obj must be locked before calling, qemud_driver must NOT be locked + * + * This must be called by anything that will change the VM state + * in any way, or anything that will use the QEMU monitor. + * + * Upon successful return, the object will have its ref count increased, + * successful calls must be followed by EndJob eventually + */ +static int qemuDomainObjBeginJob(virDomainObjPtr obj) ATTRIBUTE_RETURN_CHECK; +static int qemuDomainObjBeginJob(virDomainObjPtr obj) +{ + qemuDomainObjPrivatePtr priv = obj->privateData; + + virDomainObjRef(obj); + + while (priv->jobActive) { + if (virCondWait(&priv->jobCond, &obj->lock) < 0) { + virDomainObjUnref(obj); + virReportSystemError(NULL, errno, + "%s", _("cannot acquire job mutex")); + return -1; + } + } + priv->jobActive = 1; + + return 0; +} + +/* + * obj must be locked before calling, qemud_driver must be locked + * + * This must be called by anything that will change the VM state + * in any way, or anything that will use the QEMU monitor. + */ +static int qemuDomainObjBeginJobWithDriver(struct qemud_driver *driver, + virDomainObjPtr obj) ATTRIBUTE_RETURN_CHECK; +static int qemuDomainObjBeginJobWithDriver(struct qemud_driver *driver, + virDomainObjPtr obj) +{ + qemuDomainObjPrivatePtr priv = obj->privateData; + + virDomainObjRef(obj); + qemuDriverUnlock(driver); + + while (priv->jobActive) { + if (virCondWait(&priv->jobCond, &obj->lock) < 0) { + virDomainObjUnref(obj); + virReportSystemError(NULL, errno, + "%s", _("cannot acquire job mutex")); + return -1; + } + } + priv->jobActive = 1; + + virDomainObjUnlock(obj); + qemuDriverLock(driver); + virDomainObjLock(obj); + + return 0; +} + +/* + * obj must be locked before calling, qemud_driver does not matter + * + * To be called after completing the work associated with the + * earlier qemuDomainBeginJob() call + */ +static void qemuDomainObjEndJob(virDomainObjPtr obj) +{ + qemuDomainObjPrivatePtr priv = obj->privateData; + + priv->jobActive = 0; + virCondSignal(&priv->jobCond); + + virDomainObjUnref(obj); +} + + +/* + * obj must be locked before calling, qemud_driver must be unlocked + * + * To be called immediately before any QEMU monitor API call + * Must have alrady called qemuDomainObjBeginJob(). + * + * To be followed with qemuDomainObjExitMonitor() once complete + */ static void qemuDomainObjEnterMonitor(virDomainObjPtr obj) { qemuDomainObjPrivatePtr priv = obj->privateData; qemuMonitorLock(priv->mon); + virDomainObjUnlock(obj); } +/* obj must NOT be locked before calling, qemud_driver must be unlocked + * + * Should be paired with an earlier qemuDomainObjEnterMonitor() call + */ static void qemuDomainObjExitMonitor(virDomainObjPtr obj) { qemuDomainObjPrivatePtr priv = obj->privateData; qemuMonitorUnlock(priv->mon); + virDomainObjLock(obj); +} + + +/* + * obj must be locked before calling, qemud_driver must be locked + * + * To be called immediately before any QEMU monitor API call + * Must have alrady called qemuDomainObjBeginJob(). + * + * To be followed with qemuDomainObjExitMonitorWithDriver() once complete + */ +static void qemuDomainObjEnterMonitorWithDriver(struct qemud_driver *driver, virDomainObjPtr obj) +{ + qemuDomainObjPrivatePtr priv = obj->privateData; + + qemuMonitorLock(priv->mon); + virDomainObjUnlock(obj); + qemuDriverUnlock(driver); +} + + +/* obj must NOT be locked before calling, qemud_driver must be unlocked, + * and will be locked after returning + * + * Should be paired with an earlier qemuDomainObjEnterMonitor() call + */ +static void qemuDomainObjExitMonitorWithDriver(struct qemud_driver *driver, virDomainObjPtr obj) +{ + qemuDomainObjPrivatePtr priv = obj->privateData; + + qemuMonitorUnlock(priv->mon); + qemuDriverLock(driver); + virDomainObjLock(obj); } @@ -2677,11 +2809,15 @@ static virDomainPtr qemudDomainCreate(virConnectPtr conn, const char *xml, def = NULL; + if (qemuDomainObjBeginJobWithDriver(driver, vm) < 0) + goto cleanup; /* XXXX free the 'vm' we created ? */ + if (qemudStartVMDaemon(conn, driver, vm, NULL, -1) < 0) { + qemuDomainObjEndJob(vm); virDomainRemoveInactive(&driver->domains, vm); vm = NULL; - goto cleanup; + goto endjob; } event = virDomainEventNewFromObj(vm, @@ -2691,6 +2827,10 @@ static virDomainPtr qemudDomainCreate(virConnectPtr conn, const char *xml, dom = virGetDomain(conn, vm->def->name, vm->def->uuid); if (dom) dom->id = vm->def->id; +endjob: + if (vm) + qemuDomainObjEndJob(vm); + cleanup: virDomainDefFree(def); if (vm) @@ -2718,28 +2858,34 @@ static int qemudDomainSuspend(virDomainPtr dom) { _("no domain with matching uuid '%s'"), uuidstr); goto cleanup; } + if (qemuDomainObjBeginJobWithDriver(driver, vm) < 0) + goto cleanup; + if (!virDomainObjIsActive(vm)) { qemudReportError(dom->conn, dom, NULL, VIR_ERR_OPERATION_INVALID, "%s", _("domain is not running")); - goto cleanup; + goto endjob; } if (vm->state != VIR_DOMAIN_PAUSED) { qemuDomainObjPrivatePtr priv = vm->privateData; - qemuDomainObjEnterMonitor(vm); + qemuDomainObjEnterMonitorWithDriver(driver, vm); if (qemuMonitorStopCPUs(priv->mon) < 0) { - qemuDomainObjExitMonitor(vm); - goto cleanup; + qemuDomainObjExitMonitorWithDriver(driver, vm); + goto endjob; } - qemuDomainObjExitMonitor(vm); + qemuDomainObjExitMonitorWithDriver(driver, vm); vm->state = VIR_DOMAIN_PAUSED; event = virDomainEventNewFromObj(vm, VIR_DOMAIN_EVENT_SUSPENDED, VIR_DOMAIN_EVENT_SUSPENDED_PAUSED); } if (virDomainSaveStatus(dom->conn, driver->stateDir, vm) < 0) - goto cleanup; + goto endjob; ret = 0; +endjob: + qemuDomainObjEndJob(vm); + cleanup: if (vm) virDomainObjUnlock(vm); @@ -2767,31 +2913,38 @@ static int qemudDomainResume(virDomainPtr dom) { _("no domain with matching uuid '%s'"), uuidstr); goto cleanup; } + + if (qemuDomainObjBeginJobWithDriver(driver, vm) < 0) + goto cleanup; + if (!virDomainObjIsActive(vm)) { qemudReportError(dom->conn, dom, NULL, VIR_ERR_OPERATION_INVALID, "%s", _("domain is not running")); - goto cleanup; + goto endjob; } if (vm->state == VIR_DOMAIN_PAUSED) { qemuDomainObjPrivatePtr priv = vm->privateData; - qemuDomainObjEnterMonitor(vm); + qemuDomainObjEnterMonitorWithDriver(driver, vm); if (qemuMonitorStartCPUs(priv->mon, dom->conn) < 0) { - qemuDomainObjExitMonitor(vm); + qemuDomainObjExitMonitorWithDriver(driver, vm); if (virGetLastError() == NULL) qemudReportError(dom->conn, dom, NULL, VIR_ERR_OPERATION_FAILED, "%s", _("resume operation failed")); - goto cleanup; + goto endjob; } - qemuDomainObjExitMonitor(vm); + qemuDomainObjExitMonitorWithDriver(driver, vm); vm->state = VIR_DOMAIN_RUNNING; event = virDomainEventNewFromObj(vm, VIR_DOMAIN_EVENT_RESUMED, VIR_DOMAIN_EVENT_RESUMED_UNPAUSED); } if (virDomainSaveStatus(dom->conn, driver->stateDir, vm) < 0) - goto cleanup; + goto endjob; ret = 0; +endjob: + qemuDomainObjEndJob(vm); + cleanup: if (vm) virDomainObjUnlock(vm); @@ -2819,10 +2972,13 @@ static int qemudDomainShutdown(virDomainPtr dom) { goto cleanup; } + if (qemuDomainObjBeginJob(vm) < 0) + goto cleanup; + if (!virDomainObjIsActive(vm)) { qemudReportError(dom->conn, dom, NULL, VIR_ERR_OPERATION_INVALID, "%s", _("domain is not running")); - goto cleanup; + goto endjob; } qemuDomainObjPrivatePtr priv = vm->privateData; @@ -2830,6 +2986,9 @@ static int qemudDomainShutdown(virDomainPtr dom) { ret = qemuMonitorSystemPowerdown(priv->mon); qemuDomainObjExitMonitor(vm); +endjob: + qemuDomainObjEndJob(vm); + cleanup: if (vm) virDomainObjUnlock(vm); @@ -2852,10 +3011,14 @@ static int qemudDomainDestroy(virDomainPtr dom) { _("no domain with matching uuid '%s'"), uuidstr); goto cleanup; } + + if (qemuDomainObjBeginJobWithDriver(driver, vm) < 0) + goto cleanup; + if (!virDomainObjIsActive(vm)) { qemudReportError(dom->conn, dom, NULL, VIR_ERR_OPERATION_INVALID, "%s", _("domain is not running")); - goto cleanup; + goto endjob; } qemudShutdownVMDaemon(dom->conn, driver, vm); @@ -2863,12 +3026,17 @@ static int qemudDomainDestroy(virDomainPtr dom) { VIR_DOMAIN_EVENT_STOPPED, VIR_DOMAIN_EVENT_STOPPED_DESTROYED); if (!vm->persistent) { + qemuDomainObjEndJob(vm); virDomainRemoveInactive(&driver->domains, vm); vm = NULL; } ret = 0; +endjob: + if (vm) + qemuDomainObjEndJob(vm); + cleanup: if (vm) virDomainObjUnlock(vm); @@ -2985,25 +3153,31 @@ static int qemudDomainSetMemory(virDomainPtr dom, unsigned long newmem) { goto cleanup; } + if (qemuDomainObjBeginJob(vm) < 0) + goto cleanup; + if (virDomainObjIsActive(vm)) { qemuDomainObjPrivatePtr priv = vm->privateData; qemuDomainObjEnterMonitor(vm); int r = qemuMonitorSetBalloon(priv->mon, newmem); qemuDomainObjExitMonitor(vm); if (r < 0) - goto cleanup; + goto endjob; /* Lack of balloon support is a fatal error */ if (r == 0) { qemudReportError(dom->conn, dom, NULL, VIR_ERR_NO_SUPPORT, "%s", _("cannot set memory of an active domain")); - goto cleanup; + goto endjob; } } else { vm->def->memory = newmem; } ret = 0; +endjob: + qemuDomainObjEndJob(vm); + cleanup: if (vm) virDomainObjUnlock(vm); @@ -3044,17 +3218,28 @@ static int qemudDomainGetInfo(virDomainPtr dom, if (virDomainObjIsActive(vm)) { qemuDomainObjPrivatePtr priv = vm->privateData; - qemuDomainObjEnterMonitor(vm); - err = qemuMonitorGetBalloonInfo(priv->mon, &balloon); - qemuDomainObjExitMonitor(vm); - if (err < 0) - goto cleanup; + if (!priv->jobActive) { + if (qemuDomainObjBeginJob(vm) < 0) + goto cleanup; + + qemuDomainObjEnterMonitor(vm); + err = qemuMonitorGetBalloonInfo(priv->mon, &balloon); + qemuDomainObjExitMonitor(vm); + if (err < 0) { + qemuDomainObjEndJob(vm); + goto cleanup; + } + + if (err == 0) + /* Balloon not supported, so maxmem is always the allocation */ + info->memory = vm->def->maxmem; + else + info->memory = balloon; - if (err == 0) - /* Balloon not supported, so maxmem is always the allocation */ - info->memory = vm->def->maxmem; - else - info->memory = balloon; + qemuDomainObjEndJob(vm); + } else { + info->memory = vm->def->memory; + } } else { info->memory = vm->def->memory; } @@ -3145,22 +3330,25 @@ static int qemudDomainSave(virDomainPtr dom, goto cleanup; } + if (qemuDomainObjBeginJobWithDriver(driver, vm) < 0) + goto cleanup; + if (!virDomainObjIsActive(vm)) { qemudReportError(dom->conn, dom, NULL, VIR_ERR_OPERATION_INVALID, "%s", _("domain is not running")); - goto cleanup; + goto endjob; } /* Pause */ if (vm->state == VIR_DOMAIN_RUNNING) { qemuDomainObjPrivatePtr priv = vm->privateData; header.was_running = 1; - qemuDomainObjEnterMonitor(vm); + qemuDomainObjEnterMonitorWithDriver(driver, vm); if (qemuMonitorStopCPUs(priv->mon) < 0) { - qemuDomainObjExitMonitor(vm); - goto cleanup; + qemuDomainObjExitMonitorWithDriver(driver, vm); + goto endjob; } - qemuDomainObjExitMonitor(vm); + qemuDomainObjExitMonitorWithDriver(driver, vm); vm->state = VIR_DOMAIN_PAUSED; } @@ -3169,7 +3357,7 @@ static int qemudDomainSave(virDomainPtr dom, if (!xml) { qemudReportError(dom->conn, dom, NULL, VIR_ERR_OPERATION_FAILED, "%s", _("failed to get domain xml")); - goto cleanup; + goto endjob; } header.xml_len = strlen(xml) + 1; @@ -3177,26 +3365,26 @@ static int qemudDomainSave(virDomainPtr dom, if ((fd = open(path, O_CREAT|O_TRUNC|O_WRONLY, S_IRUSR|S_IWUSR)) < 0) { qemudReportError(dom->conn, dom, NULL, VIR_ERR_OPERATION_FAILED, _("failed to create '%s'"), path); - goto cleanup; + goto endjob; } if (safewrite(fd, &header, sizeof(header)) != sizeof(header)) { qemudReportError(dom->conn, dom, NULL, VIR_ERR_OPERATION_FAILED, "%s", _("failed to write save header")); - goto cleanup; + goto endjob; } if (safewrite(fd, xml, header.xml_len) != header.xml_len) { qemudReportError(dom->conn, dom, NULL, VIR_ERR_OPERATION_FAILED, "%s", _("failed to write xml")); - goto cleanup; + goto endjob; } if (close(fd) < 0) { virReportSystemError(dom->conn, errno, _("unable to save file %s"), path); - goto cleanup; + goto endjob; } fd = -1; @@ -3220,7 +3408,7 @@ static int qemudDomainSave(virDomainPtr dom, } if (ret < 0) - goto cleanup; + goto endjob; /* Shut it down */ qemudShutdownVMDaemon(dom->conn, driver, vm); @@ -3228,11 +3416,16 @@ static int qemudDomainSave(virDomainPtr dom, VIR_DOMAIN_EVENT_STOPPED, VIR_DOMAIN_EVENT_STOPPED_SAVED); if (!vm->persistent) { + qemuDomainObjEndJob(vm); virDomainRemoveInactive(&driver->domains, vm); vm = NULL; } +endjob: + if (vm) + qemuDomainObjEndJob(vm); + cleanup: if (fd != -1) close(fd); @@ -3272,10 +3465,13 @@ static int qemudDomainCoreDump(virDomainPtr dom, goto cleanup; } + if (qemuDomainObjBeginJob(vm) < 0) + goto cleanup; + if (!virDomainObjIsActive(vm)) { qemudReportError(dom->conn, dom, NULL, VIR_ERR_OPERATION_INVALID, "%s", _("domain is not running")); - goto cleanup; + goto endjob; } /* Migrate will always stop the VM, so once we support live dumping @@ -3290,7 +3486,7 @@ static int qemudDomainCoreDump(virDomainPtr dom, qemuDomainObjEnterMonitor(vm); if (qemuMonitorStopCPUs(priv->mon) < 0) { qemuDomainObjExitMonitor(vm); - goto cleanup; + goto endjob; } qemuDomainObjExitMonitor(vm); paused = 1; @@ -3300,7 +3496,6 @@ static int qemudDomainCoreDump(virDomainPtr dom, ret = qemuMonitorMigrateToCommand(priv->mon, 0, args, path); qemuDomainObjExitMonitor(vm); paused = 1; -cleanup: /* Since the monitor is always attached to a pty for libvirt, it will support synchronous operations so we always get here after @@ -3314,6 +3509,11 @@ cleanup: } qemuDomainObjExitMonitor(vm); } + +endjob: + qemuDomainObjEndJob(vm); + +cleanup: if (vm) virDomainObjUnlock(vm); return ret; @@ -3339,35 +3539,41 @@ static int qemudDomainSetVcpus(virDomainPtr dom, unsigned int nvcpus) { goto cleanup; } + if (qemuDomainObjBeginJob(vm) < 0) + goto cleanup; + if (virDomainObjIsActive(vm)) { qemudReportError(dom->conn, dom, NULL, VIR_ERR_OPERATION_INVALID, "%s", _("cannot change vcpu count of an active domain")); - goto cleanup; + goto endjob; } if (!(type = virDomainVirtTypeToString(vm->def->virtType))) { qemudReportError(dom->conn, dom, NULL, VIR_ERR_INTERNAL_ERROR, _("unknown virt type in domain definition '%d'"), vm->def->virtType); - goto cleanup; + goto endjob; } if ((max = qemudGetMaxVCPUs(dom->conn, type)) < 0) { qemudReportError(dom->conn, dom, NULL, VIR_ERR_INTERNAL_ERROR, "%s", _("could not determine max vcpus for the domain")); - goto cleanup; + goto endjob; } if (nvcpus > max) { qemudReportError(dom->conn, dom, NULL, VIR_ERR_INVALID_ARG, _("requested vcpus is greater than max allowable" " vcpus for the domain: %d > %d"), nvcpus, max); - goto cleanup; + goto endjob; } vm->def->vcpus = nvcpus; ret = 0; +endjob: + qemuDomainObjEndJob(vm); + cleanup: if (vm) virDomainObjUnlock(vm); @@ -3776,6 +3982,9 @@ static int qemudDomainRestore(virConnectPtr conn, } def = NULL; + if (qemuDomainObjBeginJobWithDriver(driver, vm) < 0) + goto cleanup; + if (header.version == 2) { const char *intermediate_argv[3] = { NULL, "-dc", NULL }; const char *prog = qemudSaveCompressionTypeToString(header.compressed); @@ -3783,7 +3992,7 @@ static int qemudDomainRestore(virConnectPtr conn, qemudReportError(conn, NULL, NULL, VIR_ERR_OPERATION_FAILED, _("Invalid compressed save format %d"), header.compressed); - goto cleanup; + goto endjob; } if (header.compressed != QEMUD_SAVE_FORMAT_RAW) { @@ -3795,7 +4004,7 @@ static int qemudDomainRestore(virConnectPtr conn, qemudReportError(conn, NULL, NULL, VIR_ERR_INTERNAL_ERROR, _("Failed to start decompression binary %s"), intermediate_argv[0]); - goto cleanup; + goto endjob; } } } @@ -3812,11 +4021,12 @@ static int qemudDomainRestore(virConnectPtr conn, fd = -1; if (ret < 0) { if (!vm->persistent) { + qemuDomainObjEndJob(vm); virDomainRemoveInactive(&driver->domains, vm); vm = NULL; } - goto cleanup; + goto endjob; } event = virDomainEventNewFromObj(vm, @@ -3826,20 +4036,24 @@ static int qemudDomainRestore(virConnectPtr conn, /* If it was running before, resume it now. */ if (header.was_running) { qemuDomainObjPrivatePtr priv = vm->privateData; - qemuDomainObjEnterMonitor(vm); + qemuDomainObjEnterMonitorWithDriver(driver, vm); if (qemuMonitorStartCPUs(priv->mon, conn) < 0) { if (virGetLastError() == NULL) qemudReportError(conn, NULL, NULL, VIR_ERR_OPERATION_FAILED, "%s", _("failed to resume domain")); - qemuDomainObjExitMonitor(vm); - goto cleanup; + qemuDomainObjExitMonitorWithDriver(driver,vm); + goto endjob; } - qemuDomainObjExitMonitor(vm); + qemuDomainObjExitMonitorWithDriver(driver, vm); vm->state = VIR_DOMAIN_RUNNING; virDomainSaveStatus(conn, driver->stateDir, vm); } ret = 0; +endjob: + if (vm) + qemuDomainObjEndJob(vm); + cleanup: virDomainDefFree(def); VIR_FREE(xml); @@ -3877,14 +4091,22 @@ static char *qemudDomainDumpXML(virDomainPtr dom, /* Refresh current memory based on balloon info */ if (virDomainObjIsActive(vm)) { qemuDomainObjPrivatePtr priv = vm->privateData; - qemuDomainObjEnterMonitor(vm); - err = qemuMonitorGetBalloonInfo(priv->mon, &balloon); - qemuDomainObjExitMonitor(vm); - if (err < 0) - goto cleanup; - if (err > 0) - vm->def->memory = balloon; - /* err == 0 indicates no balloon support, so ignore it */ + /* Don't delay if someone's using the monitor, just use + * existing most recent data instead */ + if (!priv->jobActive) { + if (qemuDomainObjBeginJob(vm) < 0) + goto cleanup; + + qemuDomainObjEnterMonitor(vm); + err = qemuMonitorGetBalloonInfo(priv->mon, &balloon); + qemuDomainObjExitMonitor(vm); + qemuDomainObjEndJob(vm); + if (err < 0) + goto cleanup; + if (err > 0) + vm->def->memory = balloon; + /* err == 0 indicates no balloon support, so ignore it */ + } } ret = virDomainDefFormat(dom->conn, @@ -4095,12 +4317,24 @@ static int qemudDomainStart(virDomainPtr dom) { goto cleanup; } + if (qemuDomainObjBeginJobWithDriver(driver, vm) < 0) + goto cleanup; + + if (virDomainObjIsActive(vm)) { + qemudReportError(dom->conn, dom, NULL, VIR_ERR_OPERATION_INVALID, + "%s", _("domain is already running")); + goto endjob; + } + ret = qemudStartVMDaemon(dom->conn, driver, vm, NULL, -1); if (ret != -1) event = virDomainEventNewFromObj(vm, VIR_DOMAIN_EVENT_STARTED, VIR_DOMAIN_EVENT_STARTED_BOOTED); +endjob: + qemuDomainObjEndJob(vm); + cleanup: if (vm) virDomainObjUnlock(vm); @@ -4397,6 +4631,7 @@ static char *qemudDiskDeviceName(const virConnectPtr conn, } static int qemudDomainChangeEjectableMedia(virConnectPtr conn, + struct qemud_driver *driver, virDomainObjPtr vm, virDomainDeviceDefPtr dev, unsigned int qemuCmdFlags) @@ -4450,13 +4685,13 @@ static int qemudDomainChangeEjectableMedia(virConnectPtr conn, } qemuDomainObjPrivatePtr priv = vm->privateData; - qemuDomainObjEnterMonitor(vm); + qemuDomainObjEnterMonitorWithDriver(driver, vm); if (newdisk->src) { ret = qemuMonitorChangeMedia(priv->mon, devname, newdisk->src); } else { ret = qemuMonitorEjectMedia(priv->mon, devname); } - qemuDomainObjExitMonitor(vm); + qemuDomainObjExitMonitorWithDriver(driver, vm); if (ret == 0) { VIR_FREE(origdisk->src); @@ -4470,6 +4705,7 @@ static int qemudDomainChangeEjectableMedia(virConnectPtr conn, static int qemudDomainAttachPciDiskDevice(virConnectPtr conn, + struct qemud_driver *driver, virDomainObjPtr vm, virDomainDeviceDefPtr dev) { @@ -4490,14 +4726,14 @@ static int qemudDomainAttachPciDiskDevice(virConnectPtr conn, return -1; } - qemuDomainObjEnterMonitor(vm); + qemuDomainObjEnterMonitorWithDriver(driver, vm); ret = qemuMonitorAddPCIDisk(priv->mon, dev->data.disk->src, type, &dev->data.disk->pci_addr.domain, &dev->data.disk->pci_addr.bus, &dev->data.disk->pci_addr.slot); - qemuDomainObjExitMonitor(vm); + qemuDomainObjExitMonitorWithDriver(driver, vm); if (ret == 0) virDomainDiskInsertPreAlloced(vm->def, dev->data.disk); @@ -4506,6 +4742,7 @@ static int qemudDomainAttachPciDiskDevice(virConnectPtr conn, } static int qemudDomainAttachUsbMassstorageDevice(virConnectPtr conn, + struct qemud_driver *driver, virDomainObjPtr vm, virDomainDeviceDefPtr dev) { @@ -4531,9 +4768,9 @@ static int qemudDomainAttachUsbMassstorageDevice(virConnectPtr conn, return -1; } - qemuDomainObjEnterMonitor(vm); + qemuDomainObjEnterMonitorWithDriver(driver, vm); ret = qemuMonitorAddUSBDisk(priv->mon, dev->data.disk->src); - qemuDomainObjExitMonitor(vm); + qemuDomainObjExitMonitorWithDriver(driver, vm); if (ret == 0) virDomainDiskInsertPreAlloced(vm->def, dev->data.disk); @@ -4594,24 +4831,24 @@ static int qemudDomainAttachNetDevice(virConnectPtr conn, if (virAsprintf(&tapfd_name, "fd-%s", net->hostnet_name) < 0) goto no_memory; - qemuDomainObjEnterMonitor(vm); + qemuDomainObjEnterMonitorWithDriver(driver, vm); if (qemuMonitorSendFileHandle(priv->mon, tapfd_name, tapfd) < 0) { - qemuDomainObjExitMonitor(vm); + qemuDomainObjExitMonitorWithDriver(driver, vm); goto cleanup; } - qemuDomainObjExitMonitor(vm); + qemuDomainObjExitMonitorWithDriver(driver, vm); } if (qemuBuildHostNetStr(conn, net, ' ', net->vlan, tapfd_name, &netstr) < 0) goto try_tapfd_close; - qemuDomainObjEnterMonitor(vm); + qemuDomainObjEnterMonitorWithDriver(driver, vm); if (qemuMonitorAddHostNetwork(priv->mon, netstr) < 0) { - qemuDomainObjExitMonitor(vm); + qemuDomainObjExitMonitorWithDriver(driver, vm); goto try_tapfd_close; } - qemuDomainObjExitMonitor(vm); + qemuDomainObjExitMonitorWithDriver(driver, vm); if (tapfd != -1) close(tapfd); @@ -4620,15 +4857,15 @@ static int qemudDomainAttachNetDevice(virConnectPtr conn, if (qemuBuildNicStr(conn, net, NULL, net->vlan, &nicstr) < 0) goto try_remove; - qemuDomainObjEnterMonitor(vm); + qemuDomainObjEnterMonitorWithDriver(driver, vm); if (qemuMonitorAddPCINetwork(priv->mon, nicstr, &net->pci_addr.domain, &net->pci_addr.bus, &net->pci_addr.slot) < 0) { - qemuDomainObjExitMonitor(vm); + qemuDomainObjExitMonitorWithDriver(driver, vm); goto try_remove; } - qemuDomainObjExitMonitor(vm); + qemuDomainObjExitMonitorWithDriver(driver, vm); ret = 0; @@ -4647,20 +4884,20 @@ try_remove: if (!net->hostnet_name || net->vlan == 0) VIR_WARN0(_("Unable to remove network backend\n")); else { - qemuDomainObjEnterMonitor(vm); + qemuDomainObjEnterMonitorWithDriver(driver, vm); if (qemuMonitorRemoveHostNetwork(priv->mon, net->vlan, net->hostnet_name) < 0) VIR_WARN(_("Failed to remove network backend for vlan %d, net %s"), net->vlan, net->hostnet_name); - qemuDomainObjExitMonitor(vm); + qemuDomainObjExitMonitorWithDriver(driver, vm); } goto cleanup; try_tapfd_close: if (tapfd_name) { - qemuDomainObjEnterMonitor(vm); + qemuDomainObjEnterMonitorWithDriver(driver, vm); if (qemuMonitorCloseFileHandle(priv->mon, tapfd_name) < 0) VIR_WARN(_("Failed to close tapfd with '%s'\n"), tapfd_name); - qemuDomainObjExitMonitor(vm); + qemuDomainObjExitMonitorWithDriver(driver, vm); } goto cleanup; @@ -4704,7 +4941,7 @@ static int qemudDomainAttachHostPciDevice(virConnectPtr conn, return -1; } - qemuDomainObjEnterMonitor(vm); + qemuDomainObjEnterMonitorWithDriver(driver, vm); ret = qemuMonitorAddPCIHostDevice(priv->mon, hostdev->source.subsys.u.pci.domain, hostdev->source.subsys.u.pci.bus, @@ -4713,7 +4950,7 @@ static int qemudDomainAttachHostPciDevice(virConnectPtr conn, &hostdev->source.subsys.u.pci.guest_addr.domain, &hostdev->source.subsys.u.pci.guest_addr.bus, &hostdev->source.subsys.u.pci.guest_addr.slot); - qemuDomainObjExitMonitor(vm); + qemuDomainObjExitMonitorWithDriver(driver, vm); if (ret < 0) goto error; @@ -4728,6 +4965,7 @@ error: } static int qemudDomainAttachHostUsbDevice(virConnectPtr conn, + struct qemud_driver *driver, virDomainObjPtr vm, virDomainDeviceDefPtr dev) { @@ -4739,7 +4977,7 @@ static int qemudDomainAttachHostUsbDevice(virConnectPtr conn, return -1; } - qemuDomainObjEnterMonitor(vm); + qemuDomainObjEnterMonitorWithDriver(driver, vm); if (dev->data.hostdev->source.subsys.u.usb.vendor) { ret = qemuMonitorAddUSBDeviceMatch(priv->mon, dev->data.hostdev->source.subsys.u.usb.vendor, @@ -4749,7 +4987,7 @@ static int qemudDomainAttachHostUsbDevice(virConnectPtr conn, dev->data.hostdev->source.subsys.u.usb.bus, dev->data.hostdev->source.subsys.u.usb.device); } - qemuDomainObjExitMonitor(vm); + qemuDomainObjExitMonitorWithDriver(driver, vm); if (ret != -1) vm->def->hostdevs[vm->def->nhostdevs++] = dev->data.hostdev; @@ -4781,7 +5019,7 @@ static int qemudDomainAttachHostDevice(virConnectPtr conn, case VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_PCI: return qemudDomainAttachHostPciDevice(conn, driver, vm, dev); case VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_USB: - return qemudDomainAttachHostUsbDevice(conn, vm, dev); + return qemudDomainAttachHostUsbDevice(conn, driver, vm, dev); default: qemudReportError(conn, dom, NULL, VIR_ERR_NO_SUPPORT, _("hostdev subsys type '%s' not supported"), @@ -4809,21 +5047,24 @@ static int qemudDomainAttachDevice(virDomainPtr dom, goto cleanup; } + if (qemuDomainObjBeginJobWithDriver(driver, vm) < 0) + goto cleanup; + if (!virDomainObjIsActive(vm)) { qemudReportError(dom->conn, dom, NULL, VIR_ERR_OPERATION_INVALID, "%s", _("cannot attach device on inactive domain")); - goto cleanup; + goto endjob; } dev = virDomainDeviceDefParse(dom->conn, driver->caps, vm->def, xml, VIR_DOMAIN_XML_INACTIVE); if (dev == NULL) - goto cleanup; + goto endjob; if (qemudExtractVersionInfo(vm->def->emulator, NULL, &qemuCmdFlags) < 0) - goto cleanup; + goto endjob; if (dev->type == VIR_DOMAIN_DEVICE_DISK) { if (qemuCgroupControllerActive(driver, VIR_CGROUP_CONTROLLER_DEVICES)) { @@ -4831,7 +5072,7 @@ static int qemudDomainAttachDevice(virDomainPtr dom, qemudReportError(dom->conn, NULL, NULL, VIR_ERR_INTERNAL_ERROR, _("Unable to find cgroup for %s\n"), vm->def->name); - goto cleanup; + goto endjob; } if (dev->data.disk->src != NULL && dev->data.disk->type == VIR_DOMAIN_DISK_TYPE_BLOCK && @@ -4840,7 +5081,7 @@ static int qemudDomainAttachDevice(virDomainPtr dom, qemudReportError(dom->conn, NULL, NULL, VIR_ERR_INTERNAL_ERROR, _("unable to allow device %s"), dev->data.disk->src); - goto cleanup; + goto endjob; } } @@ -4851,9 +5092,9 @@ static int qemudDomainAttachDevice(virDomainPtr dom, driver->securityDriver->domainSetSecurityImageLabel(dom->conn, vm, dev->data.disk); if (qemuDomainSetDeviceOwnership(dom->conn, driver, dev, 0) < 0) - goto cleanup; + goto endjob; - ret = qemudDomainChangeEjectableMedia(dom->conn, vm, dev, qemuCmdFlags); + ret = qemudDomainChangeEjectableMedia(dom->conn, driver, vm, dev, qemuCmdFlags); break; case VIR_DOMAIN_DISK_DEVICE_DISK: @@ -4861,13 +5102,13 @@ static int qemudDomainAttachDevice(virDomainPtr dom, driver->securityDriver->domainSetSecurityImageLabel(dom->conn, vm, dev->data.disk); if (qemuDomainSetDeviceOwnership(dom->conn, driver, dev, 0) < 0) - goto cleanup; + goto endjob; if (dev->data.disk->bus == VIR_DOMAIN_DISK_BUS_USB) { - ret = qemudDomainAttachUsbMassstorageDevice(dom->conn, vm, dev); + ret = qemudDomainAttachUsbMassstorageDevice(dom->conn, driver, vm, dev); } else if (dev->data.disk->bus == VIR_DOMAIN_DISK_BUS_SCSI || dev->data.disk->bus == VIR_DOMAIN_DISK_BUS_VIRTIO) { - ret = qemudDomainAttachPciDiskDevice(dom->conn, vm, dev); + ret = qemudDomainAttachPciDiskDevice(dom->conn, driver, vm, dev); } else { qemudReportError(dom->conn, dom, NULL, VIR_ERR_NO_SUPPORT, _("disk bus '%s' cannot be hotplugged."), @@ -4894,12 +5135,15 @@ static int qemudDomainAttachDevice(virDomainPtr dom, qemudReportError(dom->conn, dom, NULL, VIR_ERR_NO_SUPPORT, _("device type '%s' cannot be attached"), virDomainDeviceTypeToString(dev->type)); - goto cleanup; + goto endjob; } if (!ret && virDomainSaveStatus(dom->conn, driver->stateDir, vm) < 0) ret = -1; +endjob: + qemuDomainObjEndJob(vm); + cleanup: if (cgroup) virCgroupFree(&cgroup); @@ -4916,7 +5160,9 @@ cleanup: } static int qemudDomainDetachPciDiskDevice(virConnectPtr conn, - virDomainObjPtr vm, virDomainDeviceDefPtr dev) + struct qemud_driver *driver, + virDomainObjPtr vm, + virDomainDeviceDefPtr dev) { int i, ret = -1; virDomainDiskDefPtr detach = NULL; @@ -4942,7 +5188,7 @@ static int qemudDomainDetachPciDiskDevice(virConnectPtr conn, goto cleanup; } - qemuDomainObjEnterMonitor(vm); + qemuDomainObjEnterMonitorWithDriver(driver, vm); if (qemuMonitorRemovePCIDevice(priv->mon, detach->pci_addr.domain, detach->pci_addr.bus, @@ -4950,7 +5196,7 @@ static int qemudDomainDetachPciDiskDevice(virConnectPtr conn, qemuDomainObjExitMonitor(vm); goto cleanup; } - qemuDomainObjExitMonitor(vm); + qemuDomainObjExitMonitorWithDriver(driver, vm); if (vm->def->ndisks > 1) { memmove(vm->def->disks + i, @@ -4975,6 +5221,7 @@ cleanup: static int qemudDomainDetachNetDevice(virConnectPtr conn, + struct qemud_driver *driver, virDomainObjPtr vm, virDomainDeviceDefPtr dev) { @@ -5006,20 +5253,20 @@ qemudDomainDetachNetDevice(virConnectPtr conn, goto cleanup; } - qemuDomainObjEnterMonitor(vm); + qemuDomainObjEnterMonitorWithDriver(driver, vm); if (qemuMonitorRemovePCIDevice(priv->mon, detach->pci_addr.domain, detach->pci_addr.bus, detach->pci_addr.slot) < 0) { - qemuDomainObjExitMonitor(vm); + qemuDomainObjExitMonitorWithDriver(driver, vm); goto cleanup; } if (qemuMonitorRemoveHostNetwork(priv->mon, detach->vlan, detach->hostnet_name) < 0) { - qemuDomainObjExitMonitor(vm); + qemuDomainObjExitMonitorWithDriver(driver, vm); goto cleanup; } - qemuDomainObjExitMonitor(vm); + qemuDomainObjExitMonitorWithDriver(driver, vm); if (vm->def->nnets > 1) { memmove(vm->def->nets + i, @@ -5083,15 +5330,15 @@ static int qemudDomainDetachHostPciDevice(virConnectPtr conn, return -1; } - qemuDomainObjEnterMonitor(vm); + qemuDomainObjEnterMonitorWithDriver(driver, vm); if (qemuMonitorRemovePCIDevice(priv->mon, detach->source.subsys.u.pci.guest_addr.domain, detach->source.subsys.u.pci.guest_addr.bus, detach->source.subsys.u.pci.guest_addr.slot) < 0) { - qemuDomainObjExitMonitor(vm); + qemuDomainObjExitMonitorWithDriver(driver, vm); return -1; } - qemuDomainObjExitMonitor(vm); + qemuDomainObjExitMonitorWithDriver(driver, vm); ret = 0; @@ -5182,29 +5429,32 @@ static int qemudDomainDetachDevice(virDomainPtr dom, goto cleanup; } + if (qemuDomainObjBeginJobWithDriver(driver, vm) < 0) + goto cleanup; + if (!virDomainObjIsActive(vm)) { qemudReportError(dom->conn, dom, NULL, VIR_ERR_OPERATION_INVALID, "%s", _("cannot detach device on inactive domain")); - goto cleanup; + goto endjob; } dev = virDomainDeviceDefParse(dom->conn, driver->caps, vm->def, xml, VIR_DOMAIN_XML_INACTIVE); if (dev == NULL) - goto cleanup; + goto endjob; if (dev->type == VIR_DOMAIN_DEVICE_DISK && dev->data.disk->device == VIR_DOMAIN_DISK_DEVICE_DISK && (dev->data.disk->bus == VIR_DOMAIN_DISK_BUS_SCSI || dev->data.disk->bus == VIR_DOMAIN_DISK_BUS_VIRTIO)) { - ret = qemudDomainDetachPciDiskDevice(dom->conn, vm, dev); + ret = qemudDomainDetachPciDiskDevice(dom->conn, driver, vm, dev); if (driver->securityDriver) driver->securityDriver->domainRestoreSecurityImageLabel(dom->conn, vm, dev->data.disk); if (qemuDomainSetDeviceOwnership(dom->conn, driver, dev, 1) < 0) VIR_WARN0("Fail to restore disk device ownership"); } else if (dev->type == VIR_DOMAIN_DEVICE_NET) { - ret = qemudDomainDetachNetDevice(dom->conn, vm, dev); + ret = qemudDomainDetachNetDevice(dom->conn, driver, vm, dev); } else if (dev->type == VIR_DOMAIN_DEVICE_HOSTDEV) { ret = qemudDomainDetachHostDevice(dom->conn, driver, vm, dev); } else @@ -5214,6 +5464,9 @@ static int qemudDomainDetachDevice(virDomainPtr dom, if (!ret && virDomainSaveStatus(dom->conn, driver->stateDir, vm) < 0) ret = -1; +endjob: + qemuDomainObjEndJob(vm); + cleanup: virDomainDeviceDefFree(dev); if (vm) @@ -5497,10 +5750,14 @@ qemudDomainBlockStats (virDomainPtr dom, _("no domain with matching uuid '%s'"), uuidstr); goto cleanup; } + + if (qemuDomainObjBeginJob(vm) < 0) + goto cleanup; + if (!virDomainObjIsActive (vm)) { qemudReportError (dom->conn, dom, NULL, VIR_ERR_OPERATION_INVALID, "%s", _("domain is not running")); - goto cleanup; + goto endjob; } for (i = 0 ; i < vm->def->ndisks ; i++) { @@ -5513,12 +5770,12 @@ qemudDomainBlockStats (virDomainPtr dom, if (!disk) { qemudReportError (dom->conn, dom, NULL, VIR_ERR_INVALID_ARG, _("invalid path: %s"), path); - goto cleanup; + goto endjob; } qemu_dev_name = qemudDiskDeviceName(dom->conn, disk); if (!qemu_dev_name) - goto cleanup; + goto endjob; qemuDomainObjPrivatePtr priv = vm->privateData; qemuDomainObjEnterMonitor(vm); @@ -5531,6 +5788,9 @@ qemudDomainBlockStats (virDomainPtr dom, &stats->errs); qemuDomainObjExitMonitor(vm); +endjob: + qemuDomainObjEndJob(vm); + cleanup: VIR_FREE(qemu_dev_name); if (vm) @@ -5700,22 +5960,25 @@ qemudDomainMemoryPeek (virDomainPtr dom, goto cleanup; } + if (qemuDomainObjBeginJob(vm) < 0) + goto cleanup; + if (!virDomainObjIsActive(vm)) { qemudReportError(dom->conn, dom, NULL, VIR_ERR_OPERATION_INVALID, "%s", _("domain is not running")); - goto cleanup; + goto endjob; } if (virAsprintf(&tmp, driver->cacheDir, "/qemu.mem.XXXXXX") < 0) { virReportOOMError(dom->conn); - goto cleanup; + goto endjob; } /* Create a temporary filename. */ if ((fd = mkstemp (tmp)) == -1) { virReportSystemError (dom->conn, errno, _("mkstemp(\"%s\") failed"), tmp); - goto cleanup; + goto endjob; } qemuDomainObjPrivatePtr priv = vm->privateData; @@ -5723,12 +5986,12 @@ qemudDomainMemoryPeek (virDomainPtr dom, if (flags == VIR_MEMORY_VIRTUAL) { if (qemuMonitorSaveVirtualMemory(priv->mon, offset, size, tmp) < 0) { qemuDomainObjExitMonitor(vm); - goto cleanup; + goto endjob; } } else { if (qemuMonitorSavePhysicalMemory(priv->mon, offset, size, tmp) < 0) { qemuDomainObjExitMonitor(vm); - goto cleanup; + goto endjob; } } qemuDomainObjExitMonitor(vm); @@ -5738,11 +6001,14 @@ qemudDomainMemoryPeek (virDomainPtr dom, virReportSystemError (dom->conn, errno, _("failed to read temporary file " "created with template %s"), tmp); - goto cleanup; + goto endjob; } ret = 0; +endjob: + qemuDomainObjEndJob(vm); + cleanup: VIR_FREE(tmp); if (fd >= 0) close (fd); @@ -6191,13 +6457,16 @@ qemudDomainMigratePrepareTunnel(virConnectPtr dconn, } def = NULL; + if (qemuDomainObjBeginJobWithDriver(driver, vm) < 0) + goto cleanup; + /* Domain starts inactive, even if the domain XML had an id field. */ vm->def->id = -1; if (virAsprintf(&unixfile, "%s/qemu.tunnelmigrate.dest.%s", driver->stateDir, vm->def->name) < 0) { virReportOOMError (dconn); - goto cleanup; + goto endjob; } unlink(unixfile); @@ -6206,7 +6475,7 @@ qemudDomainMigratePrepareTunnel(virConnectPtr dconn, qemudReportError(dconn, NULL, NULL, VIR_ERR_INTERNAL_ERROR, _("Cannot determine QEMU argv syntax %s"), vm->def->emulator); - goto cleanup; + goto endjob; } if (qemuCmdFlags & QEMUD_CMD_FLAG_MIGRATE_QEMU_UNIX) internalret = virAsprintf(&migrateFrom, "unix:%s", unixfile); @@ -6215,11 +6484,11 @@ qemudDomainMigratePrepareTunnel(virConnectPtr dconn, else { qemudReportError(dconn, NULL, NULL, VIR_ERR_OPERATION_FAILED, "%s", _("Destination qemu is too old to support tunnelled migration")); - goto cleanup; + goto endjob; } if (internalret < 0) { virReportOOMError(dconn); - goto cleanup; + goto endjob; } /* Start the QEMU daemon, with the same command-line arguments plus * -incoming unix:/path/to/file or exec:nc -U /path/to/file @@ -6234,20 +6503,21 @@ qemudDomainMigratePrepareTunnel(virConnectPtr dconn, virDomainRemoveInactive(&driver->domains, vm); vm = NULL; } - goto cleanup; + goto endjob; } qemust = qemuStreamMigOpen(st, unixfile); if (qemust == NULL) { qemudShutdownVMDaemon(NULL, driver, vm); if (!vm->persistent) { + qemuDomainObjEndJob(vm); virDomainRemoveInactive(&driver->domains, vm); vm = NULL; } virReportSystemError(dconn, errno, _("cannot open unix socket '%s' for tunnelled migration"), unixfile); - goto cleanup; + goto endjob; } st->driver = &qemuStreamMigDrv; @@ -6258,6 +6528,10 @@ qemudDomainMigratePrepareTunnel(virConnectPtr dconn, VIR_DOMAIN_EVENT_STARTED_MIGRATED); ret = 0; +endjob: + if (vm) + qemuDomainObjEndJob(vm); + cleanup: virDomainDefFree(def); unlink(unixfile); @@ -6434,6 +6708,9 @@ qemudDomainMigratePrepare2 (virConnectPtr dconn, } def = NULL; + if (qemuDomainObjBeginJobWithDriver(driver, vm) < 0) + goto cleanup; + /* Domain starts inactive, even if the domain XML had an id field. */ vm->def->id = -1; @@ -6446,10 +6723,11 @@ qemudDomainMigratePrepare2 (virConnectPtr dconn, * should have already done that. */ if (!vm->persistent) { + qemuDomainObjEndJob(vm); virDomainRemoveInactive(&driver->domains, vm); vm = NULL; } - goto cleanup; + goto endjob; } event = virDomainEventNewFromObj(vm, @@ -6457,6 +6735,10 @@ qemudDomainMigratePrepare2 (virConnectPtr dconn, VIR_DOMAIN_EVENT_STARTED_MIGRATED); ret = 0; +endjob: + if (vm) + qemuDomainObjEndJob(vm); + cleanup: virDomainDefFree(def); if (ret != 0) { @@ -6476,6 +6758,7 @@ cleanup: * not encrypted obviously */ static int doNativeMigrate(virDomainPtr dom, + struct qemud_driver *driver, virDomainObjPtr vm, const char *uri, unsigned long flags ATTRIBUTE_UNUSED, @@ -6507,15 +6790,15 @@ static int doNativeMigrate(virDomainPtr dom, goto cleanup; } - qemuDomainObjEnterMonitor(vm); + qemuDomainObjEnterMonitorWithDriver(driver, vm); if (resource > 0 && qemuMonitorSetMigrationSpeed(priv->mon, resource) < 0) { - qemuDomainObjExitMonitor(vm); + qemuDomainObjExitMonitorWithDriver(driver, vm); goto cleanup; } if (qemuMonitorMigrateToHost(priv->mon, 0, uribits->server, uribits->port) < 0) { - qemuDomainObjExitMonitor(vm); + qemuDomainObjExitMonitorWithDriver(driver, vm); goto cleanup; } @@ -6527,10 +6810,10 @@ static int doNativeMigrate(virDomainPtr dom, &transferred, &remaining, &total) < 0) { - qemuDomainObjExitMonitor(vm); + qemuDomainObjExitMonitorWithDriver(driver, vm); goto cleanup; } - qemuDomainObjExitMonitor(vm); + qemuDomainObjExitMonitorWithDriver(driver, vm); if (status != QEMU_MONITOR_MIGRATION_STATUS_COMPLETED) { qemudReportError (dom->conn, dom, NULL, VIR_ERR_OPERATION_FAILED, @@ -6581,6 +6864,7 @@ static int doTunnelSendAll(virDomainPtr dom, } static int doTunnelMigrate(virDomainPtr dom, + struct qemud_driver *driver, virConnectPtr dconn, virDomainObjPtr vm, const char *dom_xml, @@ -6589,7 +6873,6 @@ static int doTunnelMigrate(virDomainPtr dom, const char *dname, unsigned long resource) { - struct qemud_driver *driver = dom->conn->privateData; qemuDomainObjPrivatePtr priv = vm->privateData; int client_sock = -1; int qemu_sock = -1; @@ -6687,7 +6970,7 @@ static int doTunnelMigrate(virDomainPtr dom, goto cleanup; /* 3. start migration on source */ - qemuDomainObjEnterMonitor(vm); + qemuDomainObjEnterMonitorWithDriver(driver, vm); if (qemuCmdFlags & QEMUD_CMD_FLAG_MIGRATE_QEMU_UNIX) internalret = qemuMonitorMigrateToUnix(priv->mon, 1, unixfile); else if (qemuCmdFlags & QEMUD_CMD_FLAG_MIGRATE_QEMU_EXEC) { @@ -6696,7 +6979,7 @@ static int doTunnelMigrate(virDomainPtr dom, } else { internalret = -1; } - qemuDomainObjExitMonitor(vm); + qemuDomainObjExitMonitorWithDriver(driver, vm); if (internalret < 0) { qemudReportError(dom->conn, dom, NULL, VIR_ERR_OPERATION_FAILED, "%s", _("tunnelled migration monitor command failed")); @@ -6709,16 +6992,16 @@ static int doTunnelMigrate(virDomainPtr dom, /* it is also possible that the migrate didn't fail initially, but * rather failed later on. Check the output of "info migrate" */ - qemuDomainObjEnterMonitor(vm); + qemuDomainObjEnterMonitorWithDriver(driver, vm); if (qemuMonitorGetMigrationStatus(priv->mon, &status, &transferred, &remaining, &total) < 0) { - qemuDomainObjExitMonitor(vm); + qemuDomainObjExitMonitorWithDriver(driver, vm); goto cancel; } - qemuDomainObjExitMonitor(vm); + qemuDomainObjExitMonitorWithDriver(driver, vm); if (status == QEMU_MONITOR_MIGRATION_STATUS_ERROR) { qemudReportError(dom->conn, dom, NULL, VIR_ERR_OPERATION_FAILED, @@ -6739,9 +7022,9 @@ static int doTunnelMigrate(virDomainPtr dom, cancel: if (retval != 0) { - qemuDomainObjEnterMonitor(vm); + qemuDomainObjEnterMonitorWithDriver(driver, vm); qemuMonitorMigrateCancel(priv->mon); - qemuDomainObjExitMonitor(vm); + qemuDomainObjExitMonitorWithDriver(driver, vm); } finish: @@ -6774,6 +7057,7 @@ cleanup: * virDomainMigrateVersion2 from libvirt.c, but running in source * libvirtd context, instead of client app context */ static int doNonTunnelMigrate(virDomainPtr dom, + struct qemud_driver *driver, virConnectPtr dconn, virDomainObjPtr vm, const char *dom_xml, @@ -6803,7 +7087,7 @@ static int doNonTunnelMigrate(virDomainPtr dom, _("domainMigratePrepare2 did not set uri")); } - if (doNativeMigrate(dom, vm, uri_out, flags, dname, resource) < 0) + if (doNativeMigrate(dom, driver, vm, uri_out, flags, dname, resource) < 0) goto finish; retval = 0; @@ -6822,6 +7106,7 @@ cleanup: static int doPeer2PeerMigrate(virDomainPtr dom, + struct qemud_driver *driver, virDomainObjPtr vm, const char *uri, unsigned long flags, @@ -6857,9 +7142,9 @@ static int doPeer2PeerMigrate(virDomainPtr dom, } if (flags & VIR_MIGRATE_TUNNELLED) - ret = doTunnelMigrate(dom, dconn, vm, dom_xml, uri, flags, dname, resource); + ret = doTunnelMigrate(dom, driver, dconn, vm, dom_xml, uri, flags, dname, resource); else - ret = doNonTunnelMigrate(dom, dconn, vm, dom_xml, uri, flags, dname, resource); + ret = doNonTunnelMigrate(dom, driver, dconn, vm, dom_xml, uri, flags, dname, resource); cleanup: VIR_FREE(dom_xml); @@ -6896,21 +7181,24 @@ qemudDomainMigratePerform (virDomainPtr dom, goto cleanup; } + if (qemuDomainObjBeginJobWithDriver(driver, vm) < 0) + goto cleanup; + if (!virDomainObjIsActive(vm)) { qemudReportError (dom->conn, dom, NULL, VIR_ERR_OPERATION_INVALID, "%s", _("domain is not running")); - goto cleanup; + goto endjob; } if (!(flags & VIR_MIGRATE_LIVE)) { qemuDomainObjPrivatePtr priv = vm->privateData; /* Pause domain for non-live migration */ - qemuDomainObjEnterMonitor(vm); + qemuDomainObjEnterMonitorWithDriver(driver, vm); if (qemuMonitorStopCPUs(priv->mon) < 0) { - qemuDomainObjExitMonitor(vm); - goto cleanup; + qemuDomainObjExitMonitorWithDriver(driver, vm); + goto endjob; } - qemuDomainObjExitMonitor(vm); + qemuDomainObjExitMonitorWithDriver(driver, vm); paused = 1; event = virDomainEventNewFromObj(vm, @@ -6922,12 +7210,12 @@ qemudDomainMigratePerform (virDomainPtr dom, } if ((flags & VIR_MIGRATE_TUNNELLED)) { - if (doPeer2PeerMigrate(dom, vm, uri, flags, dname, resource) < 0) + if (doPeer2PeerMigrate(dom, driver, vm, uri, flags, dname, resource) < 0) /* doTunnelMigrate already set the error, so just get out */ - goto cleanup; + goto endjob; } else { - if (doNativeMigrate(dom, vm, uri, flags, dname, resource) < 0) - goto cleanup; + if (doNativeMigrate(dom, driver, vm, uri, flags, dname, resource) < 0) + goto endjob; } /* Clean up the source domain. */ @@ -6939,16 +7227,17 @@ qemudDomainMigratePerform (virDomainPtr dom, VIR_DOMAIN_EVENT_STOPPED_MIGRATED); if (!vm->persistent || (flags & VIR_MIGRATE_UNDEFINE_SOURCE)) { virDomainDeleteConfig(dom->conn, driver->configDir, driver->autostartDir, vm); + qemuDomainObjEndJob(vm); virDomainRemoveInactive(&driver->domains, vm); vm = NULL; } ret = 0; -cleanup: +endjob: if (paused) { qemuDomainObjPrivatePtr priv = vm->privateData; /* we got here through some sort of failure; start the domain again */ - qemuDomainObjEnterMonitor(vm); + qemuDomainObjEnterMonitorWithDriver(driver, vm); if (qemuMonitorStartCPUs(priv->mon, dom->conn) < 0) { /* Hm, we already know we are in error here. We don't want to * overwrite the previous error, though, so we just throw something @@ -6957,13 +7246,16 @@ cleanup: VIR_ERROR(_("Failed to resume guest %s after failure\n"), vm->def->name); } - qemuDomainObjExitMonitor(vm); + qemuDomainObjExitMonitorWithDriver(driver, vm); event = virDomainEventNewFromObj(vm, VIR_DOMAIN_EVENT_RESUMED, VIR_DOMAIN_EVENT_RESUMED_MIGRATED); } + if (vm) + qemuDomainObjEndJob(vm); +cleanup: if (vm) virDomainObjUnlock(vm); if (event) @@ -6996,6 +7288,9 @@ qemudDomainMigrateFinish2 (virConnectPtr dconn, goto cleanup; } + if (qemuDomainObjBeginJobWithDriver(driver, vm) < 0) + goto cleanup; + /* Did the migration go as planned? If yes, return the domain * object, but if no, clean up the empty qemu process. */ @@ -7016,7 +7311,7 @@ qemudDomainMigrateFinish2 (virConnectPtr dconn, * situation and management tools are smart. */ vm = NULL; - goto cleanup; + goto endjob; } event = virDomainEventNewFromObj(vm, @@ -7035,15 +7330,15 @@ qemudDomainMigrateFinish2 (virConnectPtr dconn, * >= 0.10.6 to work properly. This isn't strictly necessary on * older qemu's, but it also doesn't hurt anything there */ - qemuDomainObjEnterMonitor(vm); + qemuDomainObjEnterMonitorWithDriver(driver, vm); if (qemuMonitorStartCPUs(priv->mon, dconn) < 0) { if (virGetLastError() == NULL) qemudReportError(dconn, NULL, NULL, VIR_ERR_INTERNAL_ERROR, "%s", _("resume operation failed")); - qemuDomainObjExitMonitor(vm); - goto cleanup; + qemuDomainObjExitMonitorWithDriver(driver, vm); + goto endjob; } - qemuDomainObjExitMonitor(vm); + qemuDomainObjExitMonitorWithDriver(driver, vm); vm->state = VIR_DOMAIN_RUNNING; event = virDomainEventNewFromObj(vm, @@ -7056,11 +7351,16 @@ qemudDomainMigrateFinish2 (virConnectPtr dconn, VIR_DOMAIN_EVENT_STOPPED, VIR_DOMAIN_EVENT_STOPPED_FAILED); if (!vm->persistent) { + qemuDomainObjEndJob(vm); virDomainRemoveInactive(&driver->domains, vm); vm = NULL; } } +endjob: + if (vm) + qemuDomainObjEndJob(vm); + cleanup: if (vm) virDomainObjUnlock(vm); -- 1.6.2.5

On Tue, Nov 03, 2009 at 02:50:05PM -0500, Daniel P. Berrange wrote:
QEMU monitor commands may sleep for a prolonged period of time. If the virDomainObjPtr or qemu driver lock is held this will needlessly block execution of many other API calls. it also prevents asynchronous monitor events from being dispatched while a monitor command is executing, because deadlock will ensure.
To resolve this, it is neccessary to release all locks while executing a monitor command. This change introduces a flag indicating that a monitor job is active, and a condition variable to synchronize access to this flag. This ensures that only a single thread can be making a state change or executing a monitor command at a time, while still allowing other API calls to be completed without blocking
* src/qemu/qemu_driver.c: Release driver and domain lock when running monitor commands * src/qemu/THREADS.txt: Document threading rules
ACK, I don't understand everything by far but looks fine. Daniel -- Daniel Veillard | libxml Gnome XML XSLT toolkit http://xmlsoft.org/ daniel@veillard.com | Rpmfind RPM search engine http://rpmfind.net/ http://veillard.com/ | virtualization library http://libvirt.org/

Some monitor commands may take a very long time to complete. It is not desirable to block other incoming API calls forever. With this change, if an existing API call is holding the job lock, additional API calls will not wait forever. They will time out after a short period of time, allowing application to retry later. * include/libvirt/virterror.h, src/util/virterror.c: Add new VIR_ERR_OPERATION_TIMEOUT error code * src/qemu/qemu_driver.c: Change to a timed condition variable wait for acquiring the monitor job lock --- include/libvirt/virterror.h | 1 + src/qemu/qemu_driver.c | 43 +++++++++++++++++++++++++++++++++++++------ src/util/virterror.c | 6 ++++++ 3 files changed, 44 insertions(+), 6 deletions(-) diff --git a/include/libvirt/virterror.h b/include/libvirt/virterror.h index fa5cac4..b7ed9e3 100644 --- a/include/libvirt/virterror.h +++ b/include/libvirt/virterror.h @@ -170,6 +170,7 @@ typedef enum { VIR_WAR_NO_SECRET, /* failed to start secret storage */ VIR_ERR_INVALID_SECRET, /* invalid secret */ VIR_ERR_NO_SECRET, /* secret not found */ + VIR_ERR_OPERATION_TIMEOUT, /* timeout occurred during operation */ } virErrorNumber; /** diff --git a/src/qemu/qemu_driver.c b/src/qemu/qemu_driver.c index b7cde56..168dffb 100644 --- a/src/qemu/qemu_driver.c +++ b/src/qemu/qemu_driver.c @@ -156,18 +156,35 @@ static void qemuDomainObjPrivateFree(void *data) * Upon successful return, the object will have its ref count increased, * successful calls must be followed by EndJob eventually */ +/* Give up waiting for mutex after 30 seconds */ +//#define QEMU_JOB_WAIT_TIME (1000ull * 30) +#define QEMU_JOB_WAIT_TIME (1000ull * 3) static int qemuDomainObjBeginJob(virDomainObjPtr obj) ATTRIBUTE_RETURN_CHECK; static int qemuDomainObjBeginJob(virDomainObjPtr obj) { qemuDomainObjPrivatePtr priv = obj->privateData; + struct timeval now; + unsigned long long then; + + if (gettimeofday(&now, NULL) < 0) { + virReportSystemError(NULL, errno, "%s", + _("cannot get time of day")); + return -1; + } + then = (now.tv_sec * 1000ull) + (now.tv_usec / 1000); + then += QEMU_JOB_WAIT_TIME; virDomainObjRef(obj); while (priv->jobActive) { - if (virCondWait(&priv->jobCond, &obj->lock) < 0) { + if (virCondWaitUntil(&priv->jobCond, &obj->lock, then) < 0) { virDomainObjUnref(obj); - virReportSystemError(NULL, errno, - "%s", _("cannot acquire job mutex")); + if (errno == ETIMEDOUT) + qemudReportError(NULL, NULL, NULL, VIR_ERR_OPERATION_TIMEOUT, + "%s", _("cannot acquire state change lock")); + else + virReportSystemError(NULL, errno, + "%s", _("cannot acquire job mutex")); return -1; } } @@ -188,15 +205,29 @@ static int qemuDomainObjBeginJobWithDriver(struct qemud_driver *driver, virDomainObjPtr obj) { qemuDomainObjPrivatePtr priv = obj->privateData; + struct timeval now; + unsigned long long then; + + if (gettimeofday(&now, NULL) < 0) { + virReportSystemError(NULL, errno, "%s", + _("cannot get time of day")); + return -1; + } + then = (now.tv_sec * 1000ull) + (now.tv_usec / 1000); + then += QEMU_JOB_WAIT_TIME; virDomainObjRef(obj); qemuDriverUnlock(driver); while (priv->jobActive) { - if (virCondWait(&priv->jobCond, &obj->lock) < 0) { + if (virCondWaitUntil(&priv->jobCond, &obj->lock, then) < 0) { virDomainObjUnref(obj); - virReportSystemError(NULL, errno, - "%s", _("cannot acquire job mutex")); + if (errno == ETIMEDOUT) + qemudReportError(NULL, NULL, NULL, VIR_ERR_OPERATION_TIMEOUT, + "%s", _("cannot acquire state change lock")); + else + virReportSystemError(NULL, errno, + "%s", _("cannot acquire job mutex")); return -1; } } diff --git a/src/util/virterror.c b/src/util/virterror.c index 10f979c..3b3956d 100644 --- a/src/util/virterror.c +++ b/src/util/virterror.c @@ -1089,6 +1089,12 @@ virErrorMsg(virErrorNumber error, const char *info) else errmsg = _("Secret not found: %s"); break; + case VIR_ERR_OPERATION_TIMEOUT: + if (info == NULL) + errmsg = _("Timed out during operation"); + else + errmsg = _("Timed out during operation: %s"); + break; } return (errmsg); } -- 1.6.2.5

On Tue, Nov 03, 2009 at 02:50:06PM -0500, Daniel P. Berrange wrote:
Some monitor commands may take a very long time to complete. It is not desirable to block other incoming API calls forever. With this change, if an existing API call is holding the job lock, additional API calls will not wait forever. They will time out after a short period of time, allowing application to retry later.
* include/libvirt/virterror.h, src/util/virterror.c: Add new VIR_ERR_OPERATION_TIMEOUT error code * src/qemu/qemu_driver.c: Change to a timed condition variable wait for acquiring the monitor job lock [...] +/* Give up waiting for mutex after 30 seconds */ +//#define QEMU_JOB_WAIT_TIME (1000ull * 30) +#define QEMU_JOB_WAIT_TIME (1000ull * 3) static int qemuDomainObjBeginJob(virDomainObjPtr obj) ATTRIBUTE_RETURN_CHECK; static int qemuDomainObjBeginJob(virDomainObjPtr obj) { qemuDomainObjPrivatePtr priv = obj->privateData; + struct timeval now; + unsigned long long then; + + if (gettimeofday(&now, NULL) < 0) { + virReportSystemError(NULL, errno, "%s", + _("cannot get time of day")); + return -1; + } + then = (now.tv_sec * 1000ull) + (now.tv_usec / 1000); + then += QEMU_JOB_WAIT_TIME;
Unless I'm mistaken looks to me that at this point then = now + 3s , not now + 30s unless the this is modified in the comment. ACK except that current code is 3s and this may be a bit short. Daniel -- Daniel Veillard | libxml Gnome XML XSLT toolkit http://xmlsoft.org/ daniel@veillard.com | Rpmfind RPM search engine http://rpmfind.net/ http://veillard.com/ | virtualization library http://libvirt.org/

On Wed, Nov 04, 2009 at 06:35:03PM +0100, Daniel Veillard wrote:
On Tue, Nov 03, 2009 at 02:50:06PM -0500, Daniel P. Berrange wrote:
Some monitor commands may take a very long time to complete. It is not desirable to block other incoming API calls forever. With this change, if an existing API call is holding the job lock, additional API calls will not wait forever. They will time out after a short period of time, allowing application to retry later.
* include/libvirt/virterror.h, src/util/virterror.c: Add new VIR_ERR_OPERATION_TIMEOUT error code * src/qemu/qemu_driver.c: Change to a timed condition variable wait for acquiring the monitor job lock [...] +/* Give up waiting for mutex after 30 seconds */ +//#define QEMU_JOB_WAIT_TIME (1000ull * 30) +#define QEMU_JOB_WAIT_TIME (1000ull * 3) static int qemuDomainObjBeginJob(virDomainObjPtr obj) ATTRIBUTE_RETURN_CHECK; static int qemuDomainObjBeginJob(virDomainObjPtr obj) { qemuDomainObjPrivatePtr priv = obj->privateData; + struct timeval now; + unsigned long long then; + + if (gettimeofday(&now, NULL) < 0) { + virReportSystemError(NULL, errno, "%s", + _("cannot get time of day")); + return -1; + } + then = (now.tv_sec * 1000ull) + (now.tv_usec / 1000); + then += QEMU_JOB_WAIT_TIME;
Unless I'm mistaken looks to me that at this point then = now + 3s , not now + 30s unless the this is modified in the comment.
Ha, yes I changed it to 3s for testing & forgot to change it back to 30 before posting the patch Daniel -- |: Red Hat, Engineering, London -o- http://people.redhat.com/berrange/ :| |: http://libvirt.org -o- http://virt-manager.org -o- http://ovirt.org :| |: http://autobuild.org -o- http://search.cpan.org/~danberr/ :| |: GnuPG: 7D3B9505 -o- F3C9 553F A1DA 4AC2 5648 23C1 B3DF F742 7D3B 9505 :|

Start for on detecting & dispatching async events fed into the QEMU monitor. In RHEL-5 fork of QEMU, the 'notify' command turns on events, and events are prefixed with a leading '#' character. * src/qemu/qemu_monitor_text.h, src/qemu/qemu_monitor_text.c: Detect and filter any async events --- src/qemu/qemu_conf.c | 5 +++-- src/qemu/qemu_monitor.c | 4 ++++ src/qemu/qemu_monitor_text.c | 33 +++++++++++++++++++++++++++++++++ src/qemu/qemu_monitor_text.h | 2 ++ 4 files changed, 42 insertions(+), 2 deletions(-) diff --git a/src/qemu/qemu_conf.c b/src/qemu/qemu_conf.c index 59873d7..b01fddc 100644 --- a/src/qemu/qemu_conf.c +++ b/src/qemu/qemu_conf.c @@ -1885,8 +1885,9 @@ int qemudBuildCommandLine(virConnectPtr conn, break; } - virBufferVSprintf(&opt, "file=%s", disk->src ? disk->src : ""); - virBufferVSprintf(&opt, ",if=%s", bus); + if (disk->src) + virBufferVSprintf(&opt, "file=%s,", disk->src ? disk->src : ""); + virBufferVSprintf(&opt, "if=%s", bus); if (disk->device == VIR_DOMAIN_DISK_DEVICE_CDROM) virBufferAddLit(&opt, ",media=cdrom"); virBufferVSprintf(&opt, ",index=%d", idx); diff --git a/src/qemu/qemu_monitor.c b/src/qemu/qemu_monitor.c index 3e6a490..501cb3f 100644 --- a/src/qemu/qemu_monitor.c +++ b/src/qemu/qemu_monitor.c @@ -499,6 +499,10 @@ qemuMonitorOpen(virDomainObjPtr vm, virDomainObjRef(vm); + if (qemuMonitorTextNotifyEnable(mon) < 0) { + VIR_INFO0("This QEMU does not support notifications"); + } + VIR_DEBUG("New mon %p fd =%d watch=%d", mon, mon->fd, mon->watch); qemuMonitorUnlock(mon); diff --git a/src/qemu/qemu_monitor_text.c b/src/qemu/qemu_monitor_text.c index db7ff57..e250abc 100644 --- a/src/qemu/qemu_monitor_text.c +++ b/src/qemu/qemu_monitor_text.c @@ -146,6 +146,7 @@ static char *qemuMonitorEscapeShell(const char *in) #define DISK_ENCRYPTION_PREFIX "(" #define DISK_ENCRYPTION_POSTFIX ") is encrypted." #define LINE_ENDING "\r\n" +#define EVENT_PREFIX "# " int qemuMonitorTextIOProcess(qemuMonitorPtr mon ATTRIBUTE_UNUSED, const char *data, @@ -174,6 +175,25 @@ int qemuMonitorTextIOProcess(qemuMonitorPtr mon ATTRIBUTE_UNUSED, /*VIR_DEBUG("Process data %d byts of data [%s]", len - used, data + used);*/ VIR_DEBUG("Process data %d byts of data", len - used); + while (STRPREFIX(data + used, EVENT_PREFIX)) { + const char *start = data + used + strlen(EVENT_PREFIX); + const char *end = strstr(start, LINE_ENDING); + int want; + char *event; + + if (!end) + goto cleanup; + + want = end - start; + + event = strndup(start, want); + + VIR_DEBUG("Woooo event [%s]", event); + VIR_FREE(event); + + used += strlen(EVENT_PREFIX) + want + strlen(LINE_ENDING); + } + /* Look for a non-zero reply followed by prompt */ if (msg && !msg->finished) { const char *end; @@ -236,6 +256,7 @@ int qemuMonitorTextIOProcess(qemuMonitorPtr mon ATTRIBUTE_UNUSED, } } +cleanup: VIR_DEBUG("Total used %d", used); return used; } @@ -380,6 +401,18 @@ qemuMonitorSendDiskPassphrase(qemuMonitorPtr mon, } int +qemuMonitorTextNotifyEnable(qemuMonitorPtr mon) +{ + char *reply; + + if (qemuMonitorCommand(mon, "notify all on", &reply) < 0) + return -1; + + VIR_FREE(reply); + return 0; +} + +int qemuMonitorTextStartCPUs(qemuMonitorPtr mon, virConnectPtr conn) { char *reply; diff --git a/src/qemu/qemu_monitor_text.h b/src/qemu/qemu_monitor_text.h index 6bca07a..abba90b 100644 --- a/src/qemu/qemu_monitor_text.h +++ b/src/qemu/qemu_monitor_text.h @@ -29,6 +29,8 @@ #include "qemu_monitor.h" +int qemuMonitorTextNotifyEnable(qemuMonitorPtr mon); + int qemuMonitorTextIOProcess(qemuMonitorPtr mon, const char *data, size_t len, -- 1.6.2.5

On Tue, Nov 03, 2009 at 02:50:07PM -0500, Daniel P. Berrange wrote:
Start for on detecting & dispatching async events fed into the QEMU monitor.
In RHEL-5 fork of QEMU, the 'notify' command turns on events, and events are prefixed with a leading '#' character.
hum, and that's likely to match upstream ? we have conditional support for rhel at configure time maybe this shoudl show up if not the case.
* src/qemu/qemu_monitor_text.h, src/qemu/qemu_monitor_text.c: Detect and filter any async events --- src/qemu/qemu_conf.c | 5 +++-- src/qemu/qemu_monitor.c | 4 ++++ src/qemu/qemu_monitor_text.c | 33 +++++++++++++++++++++++++++++++++ src/qemu/qemu_monitor_text.h | 2 ++ 4 files changed, 42 insertions(+), 2 deletions(-)
diff --git a/src/qemu/qemu_conf.c b/src/qemu/qemu_conf.c index 59873d7..b01fddc 100644 --- a/src/qemu/qemu_conf.c +++ b/src/qemu/qemu_conf.c @@ -1885,8 +1885,9 @@ int qemudBuildCommandLine(virConnectPtr conn, break; }
- virBufferVSprintf(&opt, "file=%s", disk->src ? disk->src : ""); - virBufferVSprintf(&opt, ",if=%s", bus); + if (disk->src) + virBufferVSprintf(&opt, "file=%s,", disk->src ? disk->src : ""); + virBufferVSprintf(&opt, "if=%s", bus); if (disk->device == VIR_DOMAIN_DISK_DEVICE_CDROM) virBufferAddLit(&opt, ",media=cdrom"); virBufferVSprintf(&opt, ",index=%d", idx); diff --git a/src/qemu/qemu_monitor.c b/src/qemu/qemu_monitor.c index 3e6a490..501cb3f 100644 --- a/src/qemu/qemu_monitor.c +++ b/src/qemu/qemu_monitor.c @@ -499,6 +499,10 @@ qemuMonitorOpen(virDomainObjPtr vm,
virDomainObjRef(vm);
+ if (qemuMonitorTextNotifyEnable(mon) < 0) { + VIR_INFO0("This QEMU does not support notifications"); + } + VIR_DEBUG("New mon %p fd =%d watch=%d", mon, mon->fd, mon->watch); qemuMonitorUnlock(mon);
diff --git a/src/qemu/qemu_monitor_text.c b/src/qemu/qemu_monitor_text.c index db7ff57..e250abc 100644 --- a/src/qemu/qemu_monitor_text.c +++ b/src/qemu/qemu_monitor_text.c @@ -146,6 +146,7 @@ static char *qemuMonitorEscapeShell(const char *in) #define DISK_ENCRYPTION_PREFIX "(" #define DISK_ENCRYPTION_POSTFIX ") is encrypted." #define LINE_ENDING "\r\n" +#define EVENT_PREFIX "# "
int qemuMonitorTextIOProcess(qemuMonitorPtr mon ATTRIBUTE_UNUSED, const char *data, @@ -174,6 +175,25 @@ int qemuMonitorTextIOProcess(qemuMonitorPtr mon ATTRIBUTE_UNUSED, /*VIR_DEBUG("Process data %d byts of data [%s]", len - used, data + used);*/ VIR_DEBUG("Process data %d byts of data", len - used);
+ while (STRPREFIX(data + used, EVENT_PREFIX)) { + const char *start = data + used + strlen(EVENT_PREFIX); + const char *end = strstr(start, LINE_ENDING); + int want; + char *event; + + if (!end) + goto cleanup; + + want = end - start; + + event = strndup(start, want); + + VIR_DEBUG("Woooo event [%s]", event); + VIR_FREE(event); + + used += strlen(EVENT_PREFIX) + want + strlen(LINE_ENDING); + } + /* Look for a non-zero reply followed by prompt */ if (msg && !msg->finished) { const char *end; @@ -236,6 +256,7 @@ int qemuMonitorTextIOProcess(qemuMonitorPtr mon ATTRIBUTE_UNUSED, } }
+cleanup: VIR_DEBUG("Total used %d", used); return used; } @@ -380,6 +401,18 @@ qemuMonitorSendDiskPassphrase(qemuMonitorPtr mon, }
int +qemuMonitorTextNotifyEnable(qemuMonitorPtr mon) +{ + char *reply; + + if (qemuMonitorCommand(mon, "notify all on", &reply) < 0) + return -1; + + VIR_FREE(reply); + return 0; +} + +int qemuMonitorTextStartCPUs(qemuMonitorPtr mon, virConnectPtr conn) { char *reply; diff --git a/src/qemu/qemu_monitor_text.h b/src/qemu/qemu_monitor_text.h index 6bca07a..abba90b 100644 --- a/src/qemu/qemu_monitor_text.h +++ b/src/qemu/qemu_monitor_text.h @@ -29,6 +29,8 @@
#include "qemu_monitor.h"
+int qemuMonitorTextNotifyEnable(qemuMonitorPtr mon); + int qemuMonitorTextIOProcess(qemuMonitorPtr mon, const char *data, size_t len,
ACK, Daniel -- Daniel Veillard | libxml Gnome XML XSLT toolkit http://xmlsoft.org/ daniel@veillard.com | Rpmfind RPM search engine http://rpmfind.net/ http://veillard.com/ | virtualization library http://libvirt.org/

On Wed, Nov 04, 2009 at 06:37:40PM +0100, Daniel Veillard wrote:
On Tue, Nov 03, 2009 at 02:50:07PM -0500, Daniel P. Berrange wrote:
Start for on detecting & dispatching async events fed into the QEMU monitor.
In RHEL-5 fork of QEMU, the 'notify' command turns on events, and events are prefixed with a leading '#' character.
hum, and that's likely to match upstream ? we have conditional support for rhel at configure time maybe this shoudl show up if not the case.
This patch isn't intended to be committed to main libvir repo. I just wanted to post it as proof of concept. Daniel -- |: Red Hat, Engineering, London -o- http://people.redhat.com/berrange/ :| |: http://libvirt.org -o- http://virt-manager.org -o- http://ovirt.org :| |: http://autobuild.org -o- http://search.cpan.org/~danberr/ :| |: GnuPG: 7D3B9505 -o- F3C9 553F A1DA 4AC2 5648 23C1 B3DF F742 7D3B 9505 :|

Import JSON parsing / formatting helper code based on code from http://mjson.sourceforge.net/ with some API changes to better cope with libvirt's needs. --- .x-sc_prohibit_strncpy | 1 + src/Makefile.am | 1 + src/libvirt_private.syms | 11 + src/util/json.c | 3916 ++++++++++++++++++++++++++++++++++++++++++++++ src/util/json.h | 313 ++++ 5 files changed, 4242 insertions(+), 0 deletions(-) create mode 100644 src/util/json.c create mode 100644 src/util/json.h diff --git a/.x-sc_prohibit_strncpy b/.x-sc_prohibit_strncpy index 70e2300..806217e 100644 --- a/.x-sc_prohibit_strncpy +++ b/.x-sc_prohibit_strncpy @@ -1 +1,2 @@ +^src/util/json\.c$ ^src/util/util\.c$ diff --git a/src/Makefile.am b/src/Makefile.am index 9d9ab44..9660a99 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -51,6 +51,7 @@ UTIL_SOURCES = \ util/event.c util/event.h \ util/hash.c util/hash.h \ util/iptables.c util/iptables.h \ + util/json.c util/json.h \ util/logging.c util/logging.h \ util/memory.c util/memory.h \ util/pci.c util/pci.h \ diff --git a/src/libvirt_private.syms b/src/libvirt_private.syms index 52f1a05..7ef5abe 100644 --- a/src/libvirt_private.syms +++ b/src/libvirt_private.syms @@ -260,6 +260,17 @@ virRegisterDeviceMonitor; virRegisterSecretDriver; +# json.h +json_tree_to_string; +json_parse_document; +json_new_string; +json_new_object; +json_free_value; +json_insert_pair_into_object; +json_insert_string_pair_into_object; +json_find_first_label; + + # logging.h virLogMessage; virLogGetNbFilters; diff --git a/src/util/json.c b/src/util/json.c new file mode 100644 index 0000000..ce3d99c --- /dev/null +++ b/src/util/json.c @@ -0,0 +1,3916 @@ +/*************************************************************************** + * Copyright (C) 2007 by Rui Maciel * + * rui.maciel@gmail.com * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU Library General Public License as * + * published by the Free Software Foundation; either version 2 of the * + * License, or (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU Library General Public * + * License along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ + +#include <config.h> + +#include <stdlib.h> +#include <stdio.h> + +#include "json.h" +#include "memory.h" + +enum LEX_VALUE +{ LEX_MORE = 0, + LEX_INVALID_CHARACTER, + LEX_TRUE, + LEX_FALSE, + LEX_NULL, + LEX_BEGIN_OBJECT, + LEX_END_OBJECT, + LEX_BEGIN_ARRAY, + LEX_END_ARRAY, + LEX_NAME_SEPARATOR, + LEX_VALUE_SEPARATOR, + LEX_STRING, + LEX_NUMBER, + LEX_ERROR, + LEX_MEMORY +}; + + +/* rc_string part */ + +#define RSTRING_INCSTEP 5 +#define RSTRING_DEFAULT 8 + +enum rui_string_error_codes +{ RS_MEMORY, RS_OK = 1, RS_UNKNOWN }; + +typedef enum rui_string_error_codes rstring_code; + + +static rcstring * +rcs_create (size_t length) +{ + rcstring *rcs; + if (VIR_ALLOC(rcs) < 0) + return NULL; + + rcs->max = length; + rcs->length = 0; + + if (VIR_ALLOC_N(rcs->text, rcs->max + 1) < 0) + { + VIR_FREE(rcs); + return NULL; + } + rcs->text[0] = '\0'; + + return rcs; +} + + +static void +rcs_free (rcstring ** rcs) +{ + if (*rcs != NULL) + { + VIR_FREE((*rcs)->text); + VIR_FREE(*rcs); + } + +} + + +static rstring_code +rcs_resize (rcstring * rcs, size_t length) +{ + if (VIR_REALLOC_N(rcs->text, length + 1) < 0) + { + VIR_FREE(rcs); + return RS_MEMORY; + } + rcs->max = length; + rcs->text[rcs->max] = '\0'; + return RS_OK; +} + + +static rstring_code +rcs_catcs (rcstring * pre, const char *pos, const size_t length) +{ + if (pre->max < pre->length + length) + { + if (rcs_resize (pre, pre->length + length + RSTRING_INCSTEP) != RS_OK) + return RS_MEMORY; + } + strncpy (pre->text + pre->length, pos, length); + pre->text[pre->length + length] = '\0'; + pre->length += length; + return RS_OK; +} + + +static rstring_code +rcs_catc (rcstring * pre, const char c) +{ + if (pre->max <= pre->length) + { + if (rcs_resize (pre, pre->max + RSTRING_INCSTEP) != RS_OK) + return RS_MEMORY; + } + pre->text[pre->length] = c; + pre->length++; + pre->text[pre->length] = '\0'; + return RS_OK; +} + + +static char * +rcs_unwrap (rcstring * rcs) +{ + char *out; + + if (rcs->text == NULL) + out = NULL; + else + { + if (VIR_REALLOC_N(rcs->text, strlen(rcs->text)+1) < 0) + {} /* Ignore error to shrink */ + out = rcs->text; + } + + VIR_FREE(rcs); + return out; +} + + + +static size_t +rcs_length (rcstring * rcs) +{ + /*TODO account for UTF8 */ + return rcs->length; +} + + +/* end of rc_string part */ + + +enum json_error +json_stream_parse (FILE * file, json_t ** document) +{ + char buffer[1024]; /* hard-coded value */ + unsigned int error = JSON_INCOMPLETE_DOCUMENT; + + struct json_parsing_info state; + + json_jpi_init (&state); /* initializes the json_parsing_info object */ + + while ((error == JSON_WAITING_FOR_EOF) || (error == JSON_INCOMPLETE_DOCUMENT)) + { + if (fgets (buffer, 1024, file) != NULL) + { + switch (error = json_parse_fragment (&state, buffer)) + { + case JSON_OK: + case JSON_WAITING_FOR_EOF: + case JSON_INCOMPLETE_DOCUMENT: + break; + + default: + json_free_value (&state.cursor); + return error; + break; + } + } + else + { + if (error == JSON_WAITING_FOR_EOF) + error = JSON_OK; + else + { + /*TODO refine this error code */ + error = JSON_UNKNOWN_PROBLEM; + } + } + } + + if (error == JSON_OK) + { + *document = state.cursor; + } + + return error; +} + + +json_t * +json_new_value (const enum json_value_type type) +{ + json_t *new_object; + if (VIR_ALLOC(new_object) < 0) + return NULL; + + /* initialize members */ + new_object->text = NULL; + new_object->parent = NULL; + new_object->child = NULL; + new_object->child_end = NULL; + new_object->previous = NULL; + new_object->next = NULL; + new_object->type = type; + return new_object; +} + + +json_t * +json_new_string (const char *text) +{ + json_t *new_object; + size_t length; + + if (VIR_ALLOC(new_object) < 0) + return NULL; + + /* initialize members */ + length = strlen (text) + 1; + if (VIR_ALLOC_N(new_object->text, length) < 0) + { + VIR_FREE(new_object); + return NULL; + } + strncpy (new_object->text, text, length); + new_object->parent = NULL; + new_object->child = NULL; + new_object->child_end = NULL; + new_object->previous = NULL; + new_object->next = NULL; + new_object->type = JSON_STRING; + return new_object; +} + + +json_t * +json_new_number (const char *text) +{ + json_t *new_object; + size_t length; + + /* allocate memory for the new object */ + if (VIR_ALLOC(new_object) < 0) + return NULL; + + /* initialize members */ + length = strlen (text) + 1; + if (VIR_ALLOC_N(new_object->text, length) < 0) + { + VIR_FREE(new_object); + return NULL; + } + strncpy (new_object->text, text, length); + new_object->parent = NULL; + new_object->child = NULL; + new_object->child_end = NULL; + new_object->previous = NULL; + new_object->next = NULL; + new_object->type = JSON_NUMBER; + return new_object; +} + + +json_t * +json_new_object (void) +{ + return json_new_value (JSON_OBJECT); +} + + +json_t * +json_new_array (void) +{ + return json_new_value (JSON_ARRAY); +} + + +json_t * +json_new_null (void) +{ + return json_new_value (JSON_NULL); +} + + +json_t * +json_new_true (void) +{ + return json_new_value (JSON_TRUE); +} + + +json_t * +json_new_false (void) +{ + return json_new_value (JSON_FALSE); +} + + +void +json_free_value (json_t ** value) +{ + /* free each and every child node */ + if ((*value)->child != NULL) + { + json_t *i, *j; + i = (*value)->child_end; + while (i != NULL) + { + j = i->previous; + json_free_value (&i); /*TODO replace recursive solution with an iterative one */ + i = j; + } + } + + /* fixing sibling linked list connections */ + if ((*value)->previous && (*value)->next) + { + (*value)->previous->next = (*value)->next; + (*value)->next->previous = (*value)->previous; + } + else + { + if ((*value)->previous) + { + (*value)->previous->next = NULL; + } + if ((*value)->next) + { + (*value)->next->previous = NULL; + } + } + + /*fixing parent node connections */ + if ((*value)->parent) + { + /* fix the tree connection to the first node in the children's list */ + if ((*value)->parent->child == (*value)) + { + if ((*value)->next) + { + (*value)->parent->child = (*value)->next; /* the parent node always points to the first node in the children linked list */ + } + else + { + (*value)->parent->child = NULL; + } + } + + /* fix the tree connection to the last node in the children's list */ + if ((*value)->parent->child_end == (*value)) + { + if ((*value)->previous) + { + (*value)->parent->child_end = (*value)->previous; /* the parent node always points to the last node in the children linked list */ + } + else + { + (*value)->parent->child_end = NULL; + } + } + } + + /*finally, freeing the memory allocated for this value */ + VIR_FREE((*value)->text); + VIR_FREE(*value); +} + + +enum json_error +json_insert_child (json_t * parent, json_t * child) +{ + /*TODO change the child list from FIFO to LIFO, in order to get rid of the child_end pointer */ + /* enforce tree structure correctness */ + switch (parent->type) + { + case JSON_STRING: + /* a string accepts every JSON type as a child value */ + /* therefore, the sanity check must be performed on the child node */ + switch (child->type) + { + case JSON_STRING: + case JSON_NUMBER: + case JSON_TRUE: + case JSON_FALSE: + case JSON_NULL: + if (child->child != NULL) + return JSON_BAD_TREE_STRUCTURE; + break; + + case JSON_OBJECT: + case JSON_ARRAY: + break; + + default: + return JSON_BAD_TREE_STRUCTURE; /* this part should never be reached */ + break; + } + break; + + case JSON_OBJECT: /* JSON objects may only accept JSON string objects which already have child nodes of their own */ + if (child->type != JSON_STRING) + return JSON_BAD_TREE_STRUCTURE; + break; + + case JSON_ARRAY: + switch (child->type) + { + case JSON_STRING: + case JSON_TRUE: + case JSON_FALSE: + case JSON_NULL: + case JSON_NUMBER: + if (child->child) + return JSON_BAD_TREE_STRUCTURE; + break; + + case JSON_OBJECT: + case JSON_ARRAY: + break; + + default: + return JSON_BAD_TREE_STRUCTURE; + } + break; + + default: + return JSON_BAD_TREE_STRUCTURE; + } + + child->parent = parent; + if (parent->child) + { + child->previous = parent->child_end; + parent->child_end->next = child; + parent->child_end = child; + } + else + { + parent->child = child; + parent->child_end = child; + } + + return JSON_OK; +} + + +enum json_error +json_insert_pair_into_object (json_t * parent, const char *text_label, json_t * value) +{ + enum json_error error; + json_t *label; + + /* create label json_value */ + label = json_new_string (text_label); + if (label == NULL) + return JSON_MEMORY; + + /*insert value and check for error */ + error = json_insert_child (label, value); + if (error != JSON_OK) + return error; + /*insert value and check for error */ + error = json_insert_child (parent, label); + if (error != JSON_OK) + return error; + + return JSON_OK; +} + +enum json_error +json_insert_string_pair_into_object (json_t * parent, const char *text_label, const char *value) +{ + json_t *jval; + enum json_error ret; + + if (!(jval = json_new_string(value))) + return JSON_MEMORY; + + ret = json_insert_pair_into_object(parent, text_label, jval); + if (ret != JSON_OK) + json_free_value(&jval); + + return ret; +} + +enum json_error +json_tree_to_string (json_t * root, char **text) +{ + json_t *cursor; + rcstring *output; + + cursor = root; + /* set up the output and temporary rwstrings */ + output = rcs_create (RSTRING_DEFAULT); + + /* start the convoluted fun */ +state1: /* open value */ + { + if ((cursor->previous) && (cursor != root)) /*if cursor is children and not root than it is a followup sibling */ + { + /* append comma */ + if (rcs_catc (output, ',') != RS_OK) + { + return JSON_MEMORY; + } + } + switch (cursor->type) + { + case JSON_STRING: + /* append the "text"\0, which means 1 + wcslen(cursor->text) + 1 + 1 */ + /* set the new output size */ + if (rcs_catc (output, '\"') != RS_OK) + { + return JSON_MEMORY; + } + if (rcs_catcs (output, cursor->text, strlen (cursor->text)) != RS_OK) + { + return JSON_MEMORY; + } + if (rcs_catc (output, '\"') != RS_OK) + { + return JSON_MEMORY; + } + + if (cursor->parent != NULL) + { + if (cursor->parent->type == JSON_OBJECT) /* cursor is label in label:value pair */ + { + /* error checking: if parent is object and cursor is string then cursor must have a single child */ + if (cursor->child != NULL) + { + if (rcs_catc (output, ':') != RS_OK) + { + return JSON_MEMORY; + } + } + else + { + /* malformed document tree: label without value in label:value pair */ + rcs_free (&output); + text = NULL; + return JSON_BAD_TREE_STRUCTURE; + } + } + } + else /* does not have a parent */ + { + if (cursor->child != NULL) /* is root label in label:value pair */ + { + if (rcs_catc (output, ':') != RS_OK) + { + return JSON_MEMORY; + } + } + else + { + /* malformed document tree: label without value in label:value pair */ + rcs_free (&output); + text = NULL; + return JSON_BAD_TREE_STRUCTURE; + } + } + break; + + case JSON_NUMBER: + /* must not have any children */ + /* set the new size */ + if (rcs_catcs (output, cursor->text, strlen (cursor->text)) != RS_OK) + { + return JSON_MEMORY; + } + goto state2; /* close value */ + break; + + case JSON_OBJECT: + if (rcs_catc (output, '{') != RS_OK) + { + return JSON_MEMORY; + } + + if (cursor->child) + { + cursor = cursor->child; + goto state1; /* open value */ + } + else + { + goto state2; /* close value */ + } + break; + + case JSON_ARRAY: + if (rcs_catc (output, '[') != RS_OK) + { + return JSON_MEMORY; + } + + if (cursor->child != NULL) + { + cursor = cursor->child; + goto state1; + } + else + { + goto state2; /* close value */ + } + break; + + case JSON_TRUE: + /* must not have any children */ + if (rcs_catcs (output, "true", 4) != RS_OK) + { + return JSON_MEMORY; + } + goto state2; /* close value */ + break; + + case JSON_FALSE: + /* must not have any children */ + if (rcs_catcs (output, "false", 5) != RS_OK) + { + return JSON_MEMORY; + } + goto state2; /* close value */ + break; + + case JSON_NULL: + /* must not have any children */ + if (rcs_catcs (output, "null", 4) != RS_OK) + { + return JSON_MEMORY; + } + goto state2; /* close value */ + break; + + default: + goto error; + } + if (cursor->child) + { + cursor = cursor->child; + goto state1; /* open value */ + } + else + { + /* does not have any children */ + goto state2; /* close value */ + } + } + +state2: /* close value */ + { + switch (cursor->type) + { + case JSON_OBJECT: + if (rcs_catc (output, '}') != RS_OK) + { + return JSON_MEMORY; + } + break; + + case JSON_ARRAY: + if (rcs_catc (output, ']') != RS_OK) + { + return JSON_MEMORY; + } + break; + + case JSON_STRING: + break; + case JSON_NUMBER: + break; + case JSON_TRUE: + break; + case JSON_FALSE: + break; + case JSON_NULL: + break; + default: + goto error; + } + if ((cursor->parent == NULL) || (cursor == root)) + { + goto end; + } + else if (cursor->next) + { + cursor = cursor->next; + goto state1; /* open value */ + } + else + { + cursor = cursor->parent; + goto state2; /* close value */ + } + } + +error: + { + rcs_free (&output); + return JSON_UNKNOWN_PROBLEM; + } + +end: + { + *text = rcs_unwrap (output); + return JSON_OK; + } +} + + +enum json_error +json_stream_output (FILE * file, json_t * root) +{ + json_t *cursor; + + cursor = root; + /* set up the output and temporary rwstrings */ + + /* start the convoluted fun */ +state1: /* open value */ + { + if ((cursor->previous) && (cursor != root)) /*if cursor is children and not root than it is a followup sibling */ + { + /* append comma */ + fprintf (file, ","); + } + switch (cursor->type) + { + case JSON_STRING: + /* append the "text"\0, which means 1 + wcslen(cursor->text) + 1 + 1 */ + /* set the new output size */ + fprintf (file, "\"%s\"", cursor->text); + + if (cursor->parent != NULL) + { + if (cursor->parent->type == JSON_OBJECT) /* cursor is label in label:value pair */ + { + /* error checking: if parent is object and cursor is string then cursor must have a single child */ + if (cursor->child != NULL) + { + if (fprintf (file, ":") != RS_OK) + { + return JSON_MEMORY; + } + } + else + { + /* malformed document tree: label without value in label:value pair */ + return JSON_BAD_TREE_STRUCTURE; + } + } + } + else /* does not have a parent */ + { + if (cursor->child != NULL) /* is root label in label:value pair */ + { + fprintf (file, ":"); + } + else + { + /* malformed document tree: label without value in label:value pair */ + return JSON_BAD_TREE_STRUCTURE; + } + } + break; + + case JSON_NUMBER: + /* must not have any children */ + /* set the new size */ + fprintf (file, "%s", cursor->text); + goto state2; /* close value */ + break; + + case JSON_OBJECT: + fprintf (file, "{"); + + if (cursor->child) + { + cursor = cursor->child; + goto state1; /* open value */ + } + else + { + goto state2; /* close value */ + } + break; + + case JSON_ARRAY: + fprintf (file, "["); + + if (cursor->child != NULL) + { + cursor = cursor->child; + goto state1; + } + else + { + goto state2; /* close value */ + } + break; + + case JSON_TRUE: + /* must not have any children */ + fprintf (file, "true"); + goto state2; /* close value */ + break; + + case JSON_FALSE: + /* must not have any children */ + fprintf (file, "false"); + goto state2; /* close value */ + break; + + case JSON_NULL: + /* must not have any children */ + fprintf (file, "null"); + goto state2; /* close value */ + break; + + default: + goto error; + } + if (cursor->child) + { + cursor = cursor->child; + goto state1; /* open value */ + } + else + { + /* does not have any children */ + goto state2; /* close value */ + } + } + +state2: /* close value */ + { + switch (cursor->type) + { + case JSON_OBJECT: + fprintf (file, "}"); + break; + + case JSON_ARRAY: + fprintf (file, "]"); + break; + + case JSON_STRING: + break; + case JSON_NUMBER: + break; + case JSON_TRUE: + break; + case JSON_FALSE: + break; + case JSON_NULL: + break; + default: + goto error; + } + if ((cursor->parent == NULL) || (cursor == root)) + { + goto end; + } + else if (cursor->next) + { + cursor = cursor->next; + goto state1; /* open value */ + } + else + { + cursor = cursor->parent; + goto state2; /* close value */ + } + } + +error: + { + return JSON_UNKNOWN_PROBLEM; + } + +end: + { + fprintf (file, "\n"); + return JSON_OK; + } +} + + +void +json_strip_white_spaces (char *text) +{ + size_t in, out, length; + int state; + + in = 0; + out = 0; + length = strlen (text); + state = 0; /* possible states: 0 -> document, 1 -> inside a string */ + + while (in < length) + { + switch (text[in]) + { + case '\x20': /* space */ + case '\x09': /* horizontal tab */ + case '\x0A': /* line feed or new line */ + case '\x0D': /* Carriage return */ + if (state == 1) + { + text[out++] = text[in]; + } + break; + + case '\"': + switch (state) + { + case 0: /* not inside a JSON string */ + state = 1; + break; + + case 1: /* inside a JSON string */ + if (text[in - 1] != '\\') + { + state = 0; + } + break; + } + text[out++] = text[in]; + break; + + default: + text[out++] = text[in]; + } + ++in; + } + text[out] = '\0'; +} + + +char * +json_format_string (const char *text) +{ + size_t pos = 0, text_length; + unsigned int indentation = 0; /* the current indentation level */ + unsigned int i; /* loop iterator variable */ + char loop; + + rcstring *output; + text_length = strlen (text); + + output = rcs_create (text_length); + while (pos < text_length) + { + switch (text[pos]) + { + case '\x20': + case '\x09': + case '\x0A': + case '\x0D': /* JSON insignificant white spaces */ + pos++; + break; + + case '{': + indentation++; + rcs_catcs (output, "{\n", 2); + for (i = 0; i < indentation; i++) + { + rcs_catc (output, '\t'); + } + pos++; + break; + + case '}': + indentation--; + rcs_catc (output, '\n'); + for (i = 0; i < indentation; i++) + { + rcs_catc (output, '\t'); + } + rcs_catc (output, '}'); + pos++; + break; + + case ':': + rcs_catcs (output, ": ", 2); + pos++; + break; + + case ',': + rcs_catcs (output, ",\n", 2); + for (i = 0; i < indentation; i++) + { + rcs_catc (output, '\t'); + } + pos++; + break; + + case '\"': /* open string */ + rcs_catc (output, text[pos]); + pos++; + loop = 1; /* inner string loop trigger is enabled */ + while (loop) + { + if (text[pos] == '\\') /* escaped sequence */ + { + rcs_catc (output, '\\'); + pos++; + if (text[pos] == '\"') /* don't consider a \" escaped sequence as an end of string */ + { + rcs_catc (output, '\"'); + pos++; + } + } + else if (text[pos] == '\"') /* reached end of string */ + { + loop = 0; + } + + rcs_catc (output, text[pos]); + + pos++; + if (pos >= text_length) + { + loop = 0; + } + } + break; + + default: + rcs_catc (output, text[pos]); + pos++; + break; + } + } + + return rcs_unwrap (output); +} + + +char * +json_escape (char *text) +{ + rcstring *output; + size_t i, length; + char buffer[6]; + + /* defining the temporary variables */ + length = strlen (text); + output = rcs_create (length); + if (output == NULL) + return NULL; + for (i = 0; i < length; i++) + { + if (text[i] == '\\') + { + rcs_catcs (output, "\\\\", 2); + } + else if (text[i] == '\"') + { + rcs_catcs (output, "\\\"", 2); + } + else if (text[i] == '/') + { + rcs_catcs (output, "\\/", 2); + } + else if (text[i] == '\b') + { + rcs_catcs (output, "\\b", 2); + } + else if (text[i] == '\f') + { + rcs_catcs (output, "\\f", 2); + } + else if (text[i] == '\n') + { + rcs_catcs (output, "\\n", 2); + } + else if (text[i] == '\r') + { + rcs_catcs (output, "\\r", 2); + } + else if (text[i] == '\t') + { + rcs_catcs (output, "\\t", 2); + } + else if (text[i] < 0) /* non-BMP character */ + { + rcs_catc (output, text[i]); + } + else if (text[i] < 0x20) + { + sprintf (buffer, "\\u%4.4x", text[i]); + rcs_catcs (output, buffer, 6); + } + else + { + rcs_catc (output, text[i]); + } + } + return rcs_unwrap (output); +} + + +void +json_jpi_init (struct json_parsing_info *jpi) +{ + jpi->state = 0; + jpi->lex_state = 0; + jpi->lex_text = NULL; + jpi->p = NULL; + jpi->cursor = NULL; + jpi->string_length_limit_reached = 0; +} + + +static int +lexer (const char *buffer, const char **p, unsigned int *state, rcstring ** text) +{ + if (*p == NULL) + *p = buffer; + + while (**p != '\0') + { + switch (*state) + { + + case 0: /* Root document */ + { + switch (*(*p)++) + { + case '\x20': /* space */ + case '\x09': /* horizontal tab */ + case '\x0A': /* line feed or new line */ + case '\x0D': /* Carriage return */ + break; + + case '{': + return LEX_BEGIN_OBJECT; + case '}': + return LEX_END_OBJECT; + case '[': + return LEX_BEGIN_ARRAY; + case ']': + return LEX_END_ARRAY; + case ':': + return LEX_NAME_SEPARATOR; + case ',': + return LEX_VALUE_SEPARATOR; + + case '\"': + *text = rcs_create (RSTRING_DEFAULT); + if (*text == NULL) + return LEX_MEMORY; + *state = 1; /* inside a JSON string */ + break; + + case 't': + *state = 7; /* true: 1 */ + break; + + case 'f': + *state = 10; /* false: 1 */ + break; + + case 'n': + *state = 14; /* false: 1 */ + break; + + case '-': + *text = rcs_create (RSTRING_DEFAULT); + if (*text == NULL) + return LEX_MEMORY; + if (rcs_catc (*text, '-') != RS_OK) + return LEX_MEMORY; + *state = 17; /* number: '0' */ + break; + + case '0': + *text = rcs_create (RSTRING_DEFAULT); + if (*text == NULL) + return LEX_MEMORY; + if (rcs_catc (*text, '0') != RS_OK) + return LEX_MEMORY; + *state = 18; /* number: '0' */ + break; + + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + *text = rcs_create (RSTRING_DEFAULT); + if (*text == NULL) + return LEX_MEMORY; + if (rcs_catc (*text, *(*p - 1)) != RS_OK) + return LEX_MEMORY; + *state = 19; /* number: decimal followup */ + break; + + + default: + return LEX_INVALID_CHARACTER; + } + } + break; + + case 1: /* inside a JSON string */ + { + switch (**p) + { + case 1: + case 2: + case 3: + case 4: + case 5: + case 6: + case 7: + case 8: + case 9: + case 10: /* line feed */ + case 11: + case 12: + case 13: /* carriage return */ + case 14: + case 15: + case 16: + case 17: + case 18: + case 19: + case 20: + case 21: + case 22: + case 23: + case 24: + case 25: + case 26: + case 27: + case 28: + case 29: + case 30: + case 31: + /* ASCII control characters can only be present in a JSON string if they are escaped. If not then the document is invalid */ + return LEX_INVALID_CHARACTER; + break; + + case '\"': /* close JSON string */ + /* it is expected that, in the routine that calls this function, text is set to NULL */ + *state = 0; + ++*p; + return LEX_STRING; + break; + + case '\\': + if (rcs_catc (*text, '\\') != RS_OK) + return LEX_MEMORY; + *state = 2; /* inside a JSON string: start escape sequence */ + break; + + default: + if (rcs_catc (*text, **p) != RS_OK) + return LEX_MEMORY; + } + ++*p; + } + break; + + case 2: /* inside a JSON string: start escape sequence */ + { + switch (**p) + { + case '\\': + case '\"': + case '/': + case 'b': + case 'f': + case 'n': + case 'r': + case 't': + if (rcs_catc (*text, **p) != RS_OK) + return LEX_MEMORY; + *state = 1; /* inside a JSON string */ + break; + + case 'u': + if (rcs_catc (*text, **p) != RS_OK) + return LEX_MEMORY; + *state = 3; /* inside a JSON string: escape unicode */ + break; + + default: + return LEX_INVALID_CHARACTER; + } + ++*p; + } + break; + + case 3: /*inside a JSON string: escape unicode */ + { + if ((**p >= 'a') && (**p <= 'f')) + { + if (rcs_catc (*text, **p) != RS_OK) + return LEX_MEMORY; + *state = 4; /* inside a JSON string: escape unicode */ + } + else if ((**p >= 'A') && (**p <= 'F')) + { + if (rcs_catc (*text, **p) != RS_OK) + return LEX_MEMORY; + *state = 4; /* inside a JSON string: escape unicode */ + } + else if ((**p >= '0') && (**p <= '9')) + { + if (rcs_catc (*text, **p) != RS_OK) + return LEX_MEMORY; + *state = 4; /* inside a JSON string: escape unicode */ + } + else + return LEX_INVALID_CHARACTER; + ++*p; + } + break; + + case 4: /* inside a JSON string: escape unicode */ + { + if ((**p >= 'a') && (**p <= 'f')) + { + if (rcs_catc (*text, **p) != RS_OK) + return LEX_MEMORY; + *state = 5; /* inside a JSON string: escape unicode */ + } + else if ((**p >= 'A') && (**p <= 'F')) + { + if (rcs_catc (*text, **p) != RS_OK) + return LEX_MEMORY; + *state = 5; /* inside a JSON string: escape unicode */ + } + else if ((**p >= '0') && (**p <= '9')) + { + if (rcs_catc (*text, **p) != RS_OK) + return LEX_MEMORY; + *state = 5; /* inside a JSON string: escape unicode */ + } + else + return LEX_INVALID_CHARACTER; + ++*p; + } + + case 5: /* inside a JSON string: escape unicode */ + { + if ((**p >= 'a') && (**p <= 'f')) + { + if (rcs_catc (*text, **p) != RS_OK) + return LEX_MEMORY; + *state = 6; /* inside a JSON string: escape unicode */ + } + else if ((**p >= 'A') && (**p <= 'F')) + { + if (rcs_catc (*text, **p) != RS_OK) + return LEX_MEMORY; + *state = 6; /* inside a JSON string: escape unicode */ + } + else if ((**p >= '0') && (**p <= '9')) + { + if (rcs_catc (*text, **p) != RS_OK) + return LEX_MEMORY; + *state = 6; /* inside a JSON string: escape unicode */ + } + else + return LEX_INVALID_CHARACTER; + ++*p; + } + break; + + case 6: /* inside a JSON string: escape unicode */ + { + if ((**p >= 'a') && (**p <= 'f')) + { + if (rcs_catc (*text, **p) != RS_OK) + return LEX_MEMORY; + *state = 1; /* inside a JSON string: escape unicode */ + } + else if ((**p >= 'A') && (**p <= 'F')) + { + if (rcs_catc (*text, **p) != RS_OK) + return LEX_MEMORY; + *state = 1; /* inside a JSON string: escape unicode */ + } + else if ((**p >= '0') && (**p <= '9')) + { + if (rcs_catc (*text, **p) != RS_OK) + return LEX_MEMORY; + *state = 1; /* inside a JSON string: escape unicode */ + } + else + return LEX_INVALID_CHARACTER; + ++*p; + } + break; + + case 7: /* true: 1 */ + { + switch (*(*p)++) + { + case 'r': + *state = 8; + break; + default: + return LEX_INVALID_CHARACTER; + break; + } + } + break; + + case 8: /* true: 2 */ + { + switch (*(*p)++) + { + case 'u': + *state = 9; + break; + default: + return LEX_INVALID_CHARACTER; + break; + } + } + break; + + case 9: /* true: 3 */ + { + switch (*(*p)++) + { + case 'e': + *state = 0; + return LEX_TRUE; + break; + default: + return LEX_INVALID_CHARACTER; + break; + } + } + break; + + case 10: /* false: 1 */ + { + switch (*(*p)++) + { + case 'a': + *state = 11; + break; + default: + return LEX_INVALID_CHARACTER; + break; + } + } + break; + + case 11: /* false: 2 */ + { + switch (*(*p)++) + { + case 'l': + *state = 12; + break; + default: + return LEX_INVALID_CHARACTER; + break; + } + } + break; + + case 12: /* false: 3 */ + { + switch (*(*p)++) + { + case 's': + *state = 13; + break; + default: + return LEX_INVALID_CHARACTER; + break; + } + } + break; + + case 13: /* false: 4 */ + { + switch (*(*p)++) + { + case 'e': + *state = 0; + return LEX_FALSE; + break; + default: + return LEX_INVALID_CHARACTER; + break; + } + } + break; + + case 14: /* null: 1 */ + { + switch (*(*p)++) + { + case 'u': + *state = 15; + break; + default: + return LEX_INVALID_CHARACTER; + break; + } + } + break; + + case 15: /* null: 2 */ + { + switch (*(*p)++) + { + case 'l': + *state = 16; + break; + default: + return LEX_INVALID_CHARACTER; + break; + } + } + break; + + case 16: /* null: 3 */ + { + switch (*(*p)++) + { + case 'l': + *state = 0; + return LEX_NULL; + break; + default: + return LEX_INVALID_CHARACTER; + break; + } + } + break; + + case 17: /* number: minus sign */ + { + switch (**p) + { + case '0': + if (rcs_catc (*text, **p) != RS_OK) + return LEX_MEMORY; + ++*p; + *state = 18; /* number: '0' */ + break; + + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + if (rcs_catc (*text, **p) != RS_OK) + return LEX_MEMORY; + ++*p; + *state = 19; /* number: decimal followup */ + break; + + default: + return LEX_INVALID_CHARACTER; + break; + } + } + break; + + case 18: /* number: '0' */ + { + switch (**p) + { + case '\x20': /* space */ + case '\x09': /* horizontal tab */ + case '\x0A': /* line feed or new line */ + case '\x0D': /* Carriage return */ + ++*p; + case ']': + case '}': + case ',': + *state = 0; + return LEX_NUMBER; + break; + + case '.': + if (rcs_catc (*text, **p) != RS_OK) + return LEX_MEMORY; + ++*p; + *state = 20; /* number: frac start */ + break; + + case 'e': + case 'E': + if (rcs_catc (*text, **p) != RS_OK) + return LEX_MEMORY; + ++*p; + *state = 22; /* number: exp start */ + break; + + default: + return LEX_INVALID_CHARACTER; + break; + } + } + break; + + case 19: /* number: int followup */ + { + switch (**p) + { + case '\x20': /* space */ + case '\x09': /* horizontal tab */ + case '\x0A': /* line feed or new line */ + case '\x0D': /* Carriage return */ + ++*p; + case ']': + case '}': + case ',': + *state = 0; + return LEX_NUMBER; + break; + + case '.': + if (rcs_catc (*text, **p) != RS_OK) + return LEX_MEMORY; + ++*p; + *state = 20; /* number: frac start */ + break; + + case 'e': + case 'E': + if (rcs_catc (*text, **p) != RS_OK) + return LEX_MEMORY; + ++*p; + *state = 22; /* number: exp start */ + break; + + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + if (rcs_catc (*text, **p) != RS_OK) + return LEX_MEMORY; + ++*p; + break; + + default: + return LEX_INVALID_CHARACTER; + break; + } + } + break; + + case 20: /* number: frac start */ + { + switch (**p) + { + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + if (rcs_catc (*text, **p) != RS_OK) + return LEX_MEMORY; + ++*p; + *state = 21; /* number: frac continue */ + break; + + default: + return LEX_INVALID_CHARACTER; + break; + } + } + break; + + case 21: /* number: frac continue */ + { + switch (**p) + { + case '\x20': /* space */ + case '\x09': /* horizontal tab */ + case '\x0A': /* line feed or new line */ + case '\x0D': /* Carriage return */ + ++*p; + case ']': + case '}': + case ',': + *state = 0; + return LEX_NUMBER; + break; + + case 'e': + case 'E': + if (rcs_catc (*text, **p) != RS_OK) + return LEX_MEMORY; + ++*p; + *state = 22; /* number: exp start */ + break; + + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + if (rcs_catc (*text, **p) != RS_OK) + return LEX_MEMORY; + ++*p; + break; + + default: + return LEX_INVALID_CHARACTER; + break; + } + } + break; + + case 22: /* number: exp start */ + { + switch (**p) + { + case '-': + case '+': + if (rcs_catc (*text, **p) != RS_OK) + return LEX_MEMORY; + ++*p; + *state = 23; /* number: exp continue */ + break; + + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + if (rcs_catc (*text, **p) != RS_OK) + return LEX_MEMORY; + ++*p; + *state = 24; /* number: exp end */ + break; + + default: + return LEX_INVALID_CHARACTER; + break; + } + } + break; + + case 23: /* number: exp continue */ + { + switch (**p) + { + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + if (rcs_catc (*text, **p) != RS_OK) + return LEX_MEMORY; + ++*p; + *state = 24; /* number: exp end */ + break; + + default: + return LEX_INVALID_CHARACTER; + break; + } + } + break; + + case 24: /* number: exp end */ + { + switch (**p) + { + case '\x20': /* space */ + case '\x09': /* horizontal tab */ + case '\x0A': /* line feed or new line */ + case '\x0D': /* Carriage return */ + ++*p; + case ']': + case '}': + case ',': + *state = 0; + return LEX_NUMBER; + break; + + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + if (rcs_catc (*text, **p) != RS_OK) + return LEX_MEMORY; + ++*p; + break; + + default: + return LEX_INVALID_CHARACTER; + break; + } + } + break; + + default: + printf ("*state missing: %d\n", *state); + return LEX_INVALID_CHARACTER; + } + + } + + *p = NULL; + return LEX_MORE; +} + + +enum json_error +json_parse_fragment (struct json_parsing_info *info, const char *buffer) +{ + json_t *temp = NULL; + + info->p = buffer; + while (*info->p != '\0') + { + switch (info->state) + { + case 0: /* starting point */ + { + switch (lexer (buffer, &info->p, &info->lex_state, & info->lex_text)) + { + case LEX_BEGIN_OBJECT: + info->state = 1; /* begin object */ + break; + + case LEX_BEGIN_ARRAY: + info->state = 7; /* begin array */ + break; + + case LEX_INVALID_CHARACTER: + return JSON_MALFORMED_DOCUMENT; + break; + + default: + printf ("state %d: defaulted\n", info->state); + return JSON_MALFORMED_DOCUMENT; + break; + } + } + break; + + case 1: /* open object */ + { + if (info->cursor == NULL) + { + if ((info->cursor = json_new_object ()) == NULL) + { + return JSON_MEMORY; + } + } + else + { + if ((temp = json_new_object ()) == NULL) + { + return JSON_MEMORY; + } + if (json_insert_child (info->cursor, temp) != JSON_OK) + { + return JSON_UNKNOWN_PROBLEM; + } + info->cursor = temp; + temp = NULL; + } + info->state = 2; /* just entered an object */ + } + break; + + case 2: /* opened object */ + { + switch (lexer (buffer, &info->p, &info->lex_state, & info->lex_text)) + { + case LEX_STRING: + if ((temp = json_new_value (JSON_STRING)) == NULL) + return JSON_MEMORY; + temp->text = rcs_unwrap (info->lex_text), info->lex_text = NULL; + if (json_insert_child (info->cursor, temp) != JSON_OK) + { + /*TODO return value according to the value returned from json_insert_child() */ + return JSON_UNKNOWN_PROBLEM; + } + info->cursor = temp; + temp = NULL; + info->state = 5; /* label, pre label:value separator */ + break; + + case LEX_END_OBJECT: + if (info->cursor->parent == NULL) + { + info->state = 99; /* finished document. only accept whitespaces until EOF */ + } + else + { + info->cursor = info->cursor->parent; + switch (info->cursor->type) + { + case JSON_STRING: + info->cursor = info->cursor->parent; + if (info->cursor->type != JSON_OBJECT) + { + return JSON_BAD_TREE_STRUCTURE; + } + else + { + info->state = 3; /* finished adding a field to an object */ + } + break; + + case JSON_ARRAY: + info->state = 9; + break; + + default: + return JSON_BAD_TREE_STRUCTURE; + } + } + break; + + case LEX_MORE: + return JSON_INCOMPLETE_DOCUMENT; + break; + + default: + printf ("state %d: defaulted\n", info->state); + return JSON_MALFORMED_DOCUMENT; + break; + } + } + break; + + case 3: /* finished adding a field to an object */ + { + switch (lexer (buffer, &info->p, &info->lex_state, & info->lex_text)) + { + case LEX_VALUE_SEPARATOR: + info->state = 4; /* sibling, post-object */ + break; + + case LEX_END_OBJECT: + if (info->cursor->parent == NULL) + { + info->state = 99; /* parse until EOF */ + } + else + { + info->cursor = info->cursor->parent; + switch (info->cursor->type) + { + case JSON_STRING: + info->cursor = info->cursor->parent; + if (info->cursor->type != JSON_OBJECT) + { + return JSON_BAD_TREE_STRUCTURE; + } + else + { + info->state = 3; /* finished adding a field to an object */ + } + break; + + case JSON_ARRAY: + info->state = 9; + break; + + default: + return JSON_BAD_TREE_STRUCTURE; + } + } + break; + + case LEX_MORE: + return JSON_INCOMPLETE_DOCUMENT; + break; + + default: + printf ("state %d: defaulted\n", info->state); + return JSON_MALFORMED_DOCUMENT; + break; + } + } + break; + + case 4: /* sibling, post-object */ + { + switch (lexer (buffer, &info->p, &info->lex_state, & info->lex_text)) + { + case LEX_STRING: + if ((temp = json_new_value (JSON_STRING)) == NULL) + return JSON_MEMORY; + temp->text = rcs_unwrap (info->lex_text), info->lex_text = NULL; + if (json_insert_child (info->cursor, temp) != JSON_OK) + { + return JSON_UNKNOWN_PROBLEM; + } + info->cursor = temp; + temp = NULL; + info->state = 5; + break; + + case LEX_MORE: + return JSON_INCOMPLETE_DOCUMENT; + break; + + case LEX_INVALID_CHARACTER: + return JSON_ILLEGAL_CHARACTER; + break; + + default: + printf ("state %d: defaulted\n", info->state); + return JSON_MALFORMED_DOCUMENT; + break; + } + } + break; + + case 5: /* label, pre name separator */ + { + switch (lexer (buffer, &info->p, &info->lex_state, & info->lex_text)) + { + case LEX_NAME_SEPARATOR: + info->state = 6; /* label, pos label:value separator */ + break; + + case LEX_MORE: + return JSON_INCOMPLETE_DOCUMENT; + break; + + default: + printf ("state %d: defaulted\n", info->state); + return JSON_MALFORMED_DOCUMENT; + break; + } + } + break; + + case 6: /* label, pos name separator */ + { + unsigned int value; /* to avoid redundant code */ + + switch (value = lexer (buffer, &info->p, &info->lex_state, & info->lex_text)) + { + case LEX_STRING: + if ((temp = json_new_value (JSON_STRING)) == NULL) + return JSON_MEMORY; + temp->text = rcs_unwrap (info->lex_text), info->lex_text = NULL; + if (json_insert_child (info->cursor, temp) != JSON_OK) + { + /*TODO specify the exact error message */ + return JSON_UNKNOWN_PROBLEM; + } + if (info->cursor->parent == NULL) + { + info->state = 99; /* finished document. only accepts whitespaces until EOF */ + } + else + { + info->cursor = info->cursor->parent; + } + temp = NULL; + info->state = 3; /* finished adding a field to an object */ + break; + + case LEX_NUMBER: + if ((temp = json_new_value (JSON_NUMBER)) == NULL) + return JSON_MEMORY; + temp->text = rcs_unwrap (info->lex_text), info->lex_text = NULL; + if (json_insert_child (info->cursor, temp) != JSON_OK) + { + /*TODO specify the exact error message */ + return JSON_UNKNOWN_PROBLEM; + } + if (info->cursor->parent == NULL) + { + info->state = 99; /* finished document. only accepts whitespaces until EOF */ + } + else + { + info->cursor = info->cursor->parent; + } + temp = NULL; + info->state = 3; /* finished adding a field to an object */ + break; + + case LEX_TRUE: + if ((temp = json_new_value (JSON_TRUE)) == NULL) + return JSON_MEMORY; + if (json_insert_child (info->cursor, temp) != JSON_OK) + { + /*TODO specify the exact error message */ + return JSON_UNKNOWN_PROBLEM; + } + if (info->cursor->parent == NULL) + { + info->state = 99; /* finished document. only accepts whitespaces until EOF */ + } + else + { + info->cursor = info->cursor->parent; + } + temp = NULL; + info->state = 3; /* finished adding a field to an object */ + break; + + case LEX_FALSE: + if ((temp = json_new_value (JSON_FALSE)) == NULL) + return JSON_MEMORY; + if (json_insert_child (info->cursor, temp) != JSON_OK) + { + /*TODO specify the exact error message */ + return JSON_UNKNOWN_PROBLEM; + } + if (info->cursor->parent == NULL) + { + info->state = 99; /* finished document. only accepts whitespaces until EOF */ + } + else + { + info->cursor = info->cursor->parent; + } + temp = NULL; + info->state = 3; /* finished adding a field to an object */ + break; + + case LEX_NULL: + if ((temp = json_new_value (JSON_NULL)) == NULL) + return JSON_MEMORY; + if (json_insert_child (info->cursor, temp) != JSON_OK) + { + /*TODO specify the exact error message */ + return JSON_UNKNOWN_PROBLEM; + } + if (info->cursor->parent == NULL) + { + info->state = 99; /* finished document. only accepts whitespaces until EOF */ + } + else + { + info->cursor = info->cursor->parent; + } + temp = NULL; + info->state = 3; /* finished adding a field to an object */ + break; + + case LEX_BEGIN_OBJECT: + info->state = 1; + break; + + case LEX_BEGIN_ARRAY: + info->state = 7; + break; + + case LEX_MORE: + return JSON_INCOMPLETE_DOCUMENT; + break; + + case LEX_MEMORY: + return JSON_MEMORY; + break; + + case LEX_INVALID_CHARACTER: + return JSON_ILLEGAL_CHARACTER; + break; + + default: + printf ("state %d: defaulted\n", info->state); + return JSON_MALFORMED_DOCUMENT; + break; + } + } + break; + + case 7: /* open array */ + { + if (info->cursor == NULL) + { + if ((info->cursor = json_new_array ()) == NULL) + { + return JSON_MEMORY; + } + } + else + { + if ((temp = json_new_array ()) == NULL) + { + return JSON_MEMORY; + } + if (json_insert_child (info->cursor, temp) != JSON_OK) + { + return JSON_UNKNOWN_PROBLEM; + } + info->cursor = temp; + temp = NULL; + } + info->state = 8; /* just entered an array */ + } + break; + + case 8: /* just entered an array */ + { + switch (lexer (buffer, &info->p, &info->lex_state, & info->lex_text)) + { + case LEX_STRING: + if ((temp = json_new_value (JSON_STRING)) == NULL) + return JSON_MEMORY; + temp->text = rcs_unwrap (info->lex_text), info->lex_text = NULL; + if (json_insert_child (info->cursor, temp) != JSON_OK) + { + return JSON_UNKNOWN_PROBLEM; + } + temp = NULL; + info->state = 9; /* label, pre label:value separator */ + break; + + case LEX_NUMBER: + if ((temp = json_new_value (JSON_NUMBER)) == NULL) + return JSON_MEMORY; + temp->text = rcs_unwrap (info->lex_text), info->lex_text = NULL; + if (json_insert_child (info->cursor, temp) != JSON_OK) + { + return JSON_UNKNOWN_PROBLEM; + } + temp = NULL; + info->state = 9; /* label, pre label:value separator */ + break; + + case LEX_TRUE: + if ((temp = json_new_value (JSON_TRUE)) == NULL) + return JSON_MEMORY; + if (json_insert_child (info->cursor, temp) != JSON_OK) + { + return JSON_UNKNOWN_PROBLEM; + } + info->state = 9; /* label, pre label:value separator */ + break; + + case LEX_FALSE: + if ((temp = json_new_value (JSON_FALSE)) == NULL) + return JSON_MEMORY; + if (json_insert_child (info->cursor, temp) != JSON_OK) + { + return JSON_UNKNOWN_PROBLEM; + } + info->state = 9; /* label, pre label:value separator */ + break; + + case LEX_NULL: + if ((temp = json_new_value (JSON_NULL)) == NULL) + return JSON_MEMORY; + if (json_insert_child (info->cursor, temp) != JSON_OK) + { + return JSON_UNKNOWN_PROBLEM; + } + info->state = 9; /* label, pre label:value separator */ + break; + + case LEX_BEGIN_ARRAY: + info->state = 7; /* open array */ + break; + + case LEX_END_ARRAY: + if (info->cursor->parent == NULL) + { + /*TODO implement this */ + info->state = 99; /* finished document. only accept whitespaces until EOF */ + } + else + { + info->cursor = info->cursor->parent; + switch (info->cursor->type) + { + case JSON_STRING: + if (info->cursor->parent == NULL) + return JSON_BAD_TREE_STRUCTURE; + else + { + info->cursor = info->cursor->parent; + if (info->cursor->type != JSON_OBJECT) + { + return JSON_BAD_TREE_STRUCTURE; + } + + info->state = 3; /* followup to adding child to array */ + } + break; + + case JSON_ARRAY: + info->state = 9; /* followup to adding child to array */ + break; + + default: + return JSON_BAD_TREE_STRUCTURE; + } + } + break; + + case LEX_BEGIN_OBJECT: + info->state = 1; /* open object */ + break; + + case LEX_MORE: + return JSON_INCOMPLETE_DOCUMENT; + break; + + case LEX_INVALID_CHARACTER: + return JSON_ILLEGAL_CHARACTER; + break; + + default: + printf ("state %d: defaulted\n", info->state); + return JSON_MALFORMED_DOCUMENT; + break; + } + } + break; + + case 9: /* followup to adding child to array */ + { + switch (lexer (buffer, &info->p, &info->lex_state, & info->lex_text)) + { + case LEX_VALUE_SEPARATOR: + info->state = 8; + break; + + case LEX_END_ARRAY: + if (info->cursor->parent == NULL) + { + info->state = 99; /* finished document. only accept whitespaces until EOF */ + } + else + { + info->cursor = info->cursor->parent; + switch (info->cursor->type) + { + case JSON_STRING: + if (info->cursor->parent == NULL) + { + info->state = 99; /* finished document. only accept whitespaces until EOF */ + } + else + { + info->cursor = info->cursor->parent; + if (info->cursor->type != JSON_OBJECT) + { + return JSON_BAD_TREE_STRUCTURE; + } + else + { + info->state = 3; /* followup to adding child to array */ + } + } + break; + + case JSON_ARRAY: + info->state = 9; /* followup to adding child to array */ + break; + + default: + return JSON_BAD_TREE_STRUCTURE; + } + } + break; + + case LEX_MORE: + return JSON_INCOMPLETE_DOCUMENT; + break; + + default: + printf ("state %d: defaulted\n", info->state); + return JSON_MALFORMED_DOCUMENT; + break; + } + } + break; + + case 99: /* finished document. only accept whitespaces until EOF */ + { + switch (lexer (buffer, &info->p, &info->lex_state, & info->lex_text)) + { + case LEX_MORE: + return JSON_WAITING_FOR_EOF; + break; + + case LEX_MEMORY: + return JSON_MEMORY; + break; + + default: + return JSON_MALFORMED_DOCUMENT; + break; + } + } + break; + + default: + printf ("invalid parser state %d: defaulted\n", info->state); + return JSON_UNKNOWN_PROBLEM; + } + } + info->p = NULL; + if (info->state == 99) + return JSON_WAITING_FOR_EOF; + else + return JSON_INCOMPLETE_DOCUMENT; +} + + + +enum json_error +json_parse_document (json_t ** root, const char *text) +{ + enum json_error error; + struct json_parsing_info *jpi; + + /* initialize the parsing structure */ + if (VIR_ALLOC(jpi) < 0) + { + return JSON_MEMORY; + } + json_jpi_init (jpi); + + error = json_parse_fragment (jpi, text); + if ((error == JSON_WAITING_FOR_EOF) || (error == JSON_OK)) + { + *root = jpi->cursor; + VIR_FREE(jpi); + return JSON_OK; + } + else + { + VIR_FREE(jpi); + return error; + } +} + + +enum json_error +json_saxy_parse (struct json_saxy_parser_status *jsps, struct json_saxy_functions *jsf, char c) +{ + /*TODO handle a string instead of a single char */ + /* temp variables */ + rcstring *temp; + + temp = NULL; + + /* goto where we left off */ + switch (jsps->state) + { + case 0: /* general state. everything goes. */ + goto state0; + break; + case 1: /* parse string */ + goto state1; + break; + case 2: /* parse string: escaped character */ + goto state2; + break; + case 3: /* parse string: escaped unicode 1 */ + goto state3; + break; + case 4: /* parse string: escaped unicode 2 */ + goto state4; + break; + case 5: /* parse string: escaped unicode 3 */ + goto state5; + break; + case 6: /* parse string: escaped unicode 4 */ + goto state6; + break; + case 7: /* parse true: tr */ + goto state7; + break; + case 8: /* parse true: tru */ + goto state8; + break; + case 9: /* parse true: true */ + goto state9; + break; + case 10: /* parse false: fa */ + goto state10; + break; + case 11: /* parse false: fal */ + goto state11; + break; + case 12: /* parse false: fals */ + goto state12; + break; + case 13: /* parse false: false */ + goto state13; + break; + case 14: /* parse null: nu */ + goto state14; + break; + case 15: /* parse null: nul */ + goto state15; + break; + case 16: /* parse null: null */ + goto state16; + break; + case 17: /* parse number: 0 */ + goto state17; + break; + case 18: /* parse number: start fraccional part */ + goto state18; + break; + case 19: /* parse number: fraccional part */ + goto state19; + break; + case 20: /* parse number: start exponent part */ + goto state20; + break; + case 21: /* parse number: exponent part */ + goto state21; + break; + case 22: /* parse number: exponent sign part */ + goto state22; + break; + case 23: /* parse number: start negative */ + goto state23; + break; + case 24: /* parse number: decimal part */ + goto state24; + break; + case 25: /* open object */ + goto state25; + break; + case 26: /* close object/array */ + goto state26; + break; + case 27: /* sibling followup */ + goto state27; + break; + + default: /* oops... this should never be reached */ + return JSON_UNKNOWN_PROBLEM; + } + +state0: /* starting point */ + { + switch (c) + { + case '\x20': + case '\x09': + case '\x0A': + case '\x0D': /* JSON insignificant white spaces */ + break; + + case '\"': /* starting a string */ + jsps->string_length_limit_reached = 0; + jsps->state = 1; + break; + + case '{': + if (jsf->open_object != NULL) + jsf->open_object (); + jsps->state = 25; /*open object */ + break; + + case '}': + if (jsf->close_object != NULL) + jsf->close_object (); + jsps->state = 26; /* close object/array */ + break; + + case '[': + if (jsf->open_array != NULL) + jsf->open_array (); +/* jsps->state = 0; // redundant*/ + break; + + case ']': + if (jsf->close_array != NULL) + jsf->close_array (); + jsps->state = 26; /* close object/array */ + break; + + case 't': + jsps->state = 7; /* parse true: tr */ + break; + + case 'f': + jsps->state = 10; /* parse false: fa */ + break; + + case 'n': + jsps->state = 14; /* parse null: nu */ + break; + + case ':': + if (jsf->label_value_separator != NULL) + jsf->label_value_separator (); +/* jsps->state = 0; // redundant*/ + break; + + case ',': + if (jsf->sibling_separator != NULL) + jsf->sibling_separator (); + jsps->state = 27; /* sibling followup */ + break; + + case '0': + jsps->string_length_limit_reached = 0; + jsps->state = 17; /* parse number: 0 */ + if ((jsps->temp = rcs_create (5)) == NULL) + { + return JSON_MEMORY; + } + if (rcs_catc ((jsps->temp), '0') != RS_OK) + { + return JSON_MEMORY; + } + break; + + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + jsps->string_length_limit_reached = 0; + jsps->state = 24; /* parse number: decimal */ + if ((jsps->temp = rcs_create (5)) == NULL) + { + return JSON_MEMORY; + } + if (rcs_catc ((jsps->temp), c) != RS_OK) + { + return JSON_MEMORY; + } + break; + + case '-': + jsps->string_length_limit_reached = 0; + jsps->state = 23; /* number: */ + jsps->temp = NULL; + if ((jsps->temp = rcs_create (5)) == NULL) + { + return JSON_MEMORY; + } + if (rcs_catc ((jsps->temp), '-') != RS_OK) + { + return JSON_MEMORY; + } + + break; + + default: + return JSON_ILLEGAL_CHARACTER; + break; + } + return JSON_OK; + } + +state1: /* parse string */ + { + switch (c) + { + case '\\': + if (!jsps->string_length_limit_reached) + { + if (rcs_length ((jsps->temp)) < JSON_MAX_STRING_LENGTH - 1) /* check if there is space for a two character escape sequence */ + { + if (rcs_catc ((jsps->temp), '\\') != RS_OK) + { + return JSON_MEMORY; + } + } + else + { + jsps->string_length_limit_reached = 1; + } + } + jsps->state = 2; /* parse string: escaped character */ + break; + + case '\"': /* end of string */ + if ((jsps->temp) != NULL) + { + jsps->state = 0; /* starting point */ + if (jsf->new_string != NULL) + jsf->new_string (((jsps->temp))->text); /*copied or integral? */ + rcs_free (& jsps->temp); + } + else + return JSON_UNKNOWN_PROBLEM; + break; + + default: + if (!jsps->string_length_limit_reached) + { + if (rcs_length ((jsps->temp)) < JSON_MAX_STRING_LENGTH) /* check if there is space for a two character escape sequence */ + { + if (rcs_catc ((jsps->temp), c) != RS_OK) + { + return JSON_MEMORY; + } + } + else + { + jsps->string_length_limit_reached = 1; + } + } + break; + } + return JSON_OK; + } + +state2: /* parse string: escaped character */ + { + switch (c) + { + case '\"': + case '\\': + case '/': + case 'b': + case 'f': + case 'n': + case 'r': + case 't': + if (!jsps->string_length_limit_reached) + { + if (rcs_length ((jsps->temp)) < JSON_MAX_STRING_LENGTH) + { + if (rcs_catc ((jsps->temp), c) != RS_OK) + { + return JSON_MEMORY; + } + } + else + { + jsps->string_length_limit_reached = 1; + } + } + break; + + case 'u': + if (!jsps->string_length_limit_reached) + { + if (rcs_length ((jsps->temp)) < JSON_MAX_STRING_LENGTH - 4) + { + if (rcs_catc ((jsps->temp), 'u') != RS_OK) + { + return JSON_MEMORY; + } + } + else + { + jsps->string_length_limit_reached = 1; + } + } + jsps->state = 3; /* parse string: escaped unicode 1; */ + break; + + default: + return JSON_ILLEGAL_CHARACTER; + break; + } + return JSON_OK; + } + +state3: /* parse string: escaped unicode 1 */ + { + switch (c) + { + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + case 'a': + case 'b': + case 'c': + case 'd': + case 'e': + case 'f': + case 'A': + case 'B': + case 'C': + case 'D': + case 'E': + case 'F': + if (!jsps->string_length_limit_reached) + { + if (rcs_length ((jsps->temp)) < JSON_MAX_STRING_LENGTH - 3) + { + if (rcs_catc ((jsps->temp), 'u') != RS_OK) + { + return JSON_MEMORY; + } + } + else + { + jsps->string_length_limit_reached = 1; + } + } + jsps->state = 4; /* parse string. escaped unicode 2 */ + break; + + default: + return JSON_ILLEGAL_CHARACTER; + } + return JSON_OK; + } + +state4: /* parse string: escaped unicode 2 */ + { + switch (c) + { + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + case 'a': + case 'b': + case 'c': + case 'd': + case 'e': + case 'f': + case 'A': + case 'B': + case 'C': + case 'D': + case 'E': + case 'F': + if (!jsps->string_length_limit_reached) + { + if (rcs_length ((jsps->temp)) < JSON_MAX_STRING_LENGTH - 2) + { + if (rcs_catc ((jsps->temp), c) != RS_OK) + { + return JSON_MEMORY; + } + } + else + { + jsps->string_length_limit_reached = 1; + } + } + jsps->state = 5; /* parse string. escaped unicode 3 */ + break; + + default: + return JSON_ILLEGAL_CHARACTER; + } + return JSON_OK; + } + +state5: /* parse string: escaped unicode 3 */ + { + switch (c) + { + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + case 'a': + case 'b': + case 'c': + case 'd': + case 'e': + case 'f': + case 'A': + case 'B': + case 'C': + case 'D': + case 'E': + case 'F': + if (!jsps->string_length_limit_reached) + { + if (rcs_length ((jsps->temp)) < JSON_MAX_STRING_LENGTH - 1) + { + if (rcs_catc ((jsps->temp), c) != RS_OK) + { + return JSON_MEMORY; + } + } + else + { + jsps->string_length_limit_reached = 1; + } + } + jsps->state = 6; /* parse string. escaped unicode 4 */ + break; + + default: + return JSON_ILLEGAL_CHARACTER; + } + return JSON_OK; + } + +state6: /* parse string: escaped unicode 4 */ + { + switch (c) + { + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + case 'a': + case 'b': + case 'c': + case 'd': + case 'e': + case 'f': + case 'A': + case 'B': + case 'C': + case 'D': + case 'E': + case 'F': + if (!jsps->string_length_limit_reached) + { + if (rcs_length ((jsps->temp)) < JSON_MAX_STRING_LENGTH) + { + if (rcs_catc ((jsps->temp), c) != RS_OK) + { + return JSON_MEMORY; + } + } + else + { + jsps->string_length_limit_reached = 1; + } + } + jsps->state = 1; /* parse string */ + break; + + default: + return JSON_ILLEGAL_CHARACTER; + } + return JSON_OK; + } + +state7: /* parse true: tr */ + { + if (c != 'r') + { + return JSON_ILLEGAL_CHARACTER; + } + + jsps->state = 8; /* parse true: tru */ + return JSON_OK; + } + +state8: /* parse true: tru */ + { + if (c != 'u') + { + return JSON_ILLEGAL_CHARACTER; + } + + jsps->state = 9; /* parse true: true */ + return JSON_OK; + } + +state9: /* parse true: true */ + { + if (c != 'e') + { + return JSON_ILLEGAL_CHARACTER; + } + + jsps->state = 0; /* back to general state. */ + if (jsf->new_true != NULL) + jsf->new_true (); + return JSON_OK; + } + +state10: /* parse false: fa */ + { + if (c != 'a') + { + return JSON_ILLEGAL_CHARACTER; + } + + jsps->state = 11; /* parse true: fal */ + return JSON_OK; + } + +state11: /* parse false: fal */ + { + if (c != 'l') + { + return JSON_ILLEGAL_CHARACTER; + } + + jsps->state = 12; /* parse true: fals */ + return JSON_OK; + } + +state12: /* parse false: fals */ + { + if (c != 's') + { + return JSON_ILLEGAL_CHARACTER; + } + + jsps->state = 13; /* parse true: false */ + return JSON_OK; + } + +state13: /* parse false: false */ + { + if (c != 'e') + { + return JSON_ILLEGAL_CHARACTER; + } + + jsps->state = 0; /* general state. everything goes. */ + if (jsf->new_false != NULL) + jsf->new_false (); + return JSON_OK; + } + +state14: /* parse null: nu */ + { + if (c != 'u') + { + return JSON_ILLEGAL_CHARACTER; + } + + jsps->state = 15; /* parse null: nul */ + return JSON_OK; + } + +state15: /* parse null: nul */ + { + if (c != 'l') + { + return JSON_ILLEGAL_CHARACTER; + } + + jsps->state = 16; /* parse null: null */ + return JSON_OK; + } + +state16: /* parse null: null */ + { + if (c != 'l') + { + return JSON_ILLEGAL_CHARACTER; + } + + jsps->state = 0; /* general state. everything goes. */ + if (jsf->new_null != NULL) + jsf->new_null (); + return JSON_OK; + } + +state17: /* parse number: 0 */ + { + switch (c) + { + case '.': + if ((jsps->temp = rcs_create (5)) == NULL) + { + return JSON_MEMORY; + } + if (rcs_catc ((jsps->temp), '.') != RS_OK) + { + return JSON_MEMORY; + } + jsps->state = 18; /* parse number: fraccional part */ + break; + + case '\x20': + case '\x09': + case '\x0A': + case '\x0D': /* JSON insignificant white spaces */ + if ((jsps->temp) == NULL) + return JSON_MEMORY; + if (jsf->new_number != NULL) + { + jsf->new_number ((jsps->temp)->text); + } + rcs_free (& jsps->temp); + + jsps->state = 0; + break; + + case '}': + if ((jsps->temp) == NULL) + return JSON_MEMORY; + if (jsf->new_number != NULL) + { + jsf->new_number ((jsps->temp)->text); + } + rcs_free (& jsps->temp); + + if (jsf->open_object != NULL) + jsf->close_object (); + jsps->state = 26; /* close object/array */ + break; + + case ']': + + if ((jsps->temp) == NULL) + return JSON_MEMORY; + if (jsf->new_number != NULL) + { + jsf->new_number ((jsps->temp)->text); + } + rcs_free (& jsps->temp); + + if (jsf->open_object != NULL) + jsf->close_array (); + jsps->state = 26; /* close object/array */ + break; + + case ',': + + if ((jsps->temp) == NULL) + return JSON_MEMORY; + if (jsf->new_number != NULL) + { + jsf->new_number ((jsps->temp)->text); + } + rcs_free (& jsps->temp); + + if (jsf->open_object != NULL) + jsf->label_value_separator (); + jsps->state = 27; /* sibling followup */ + break; + + default: + return JSON_ILLEGAL_CHARACTER; + break; + } + + return JSON_OK; + } + +state18: /* parse number: start fraccional part */ + { + switch (c) + { + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + if (!jsps->string_length_limit_reached) + { + if (rcs_length ((jsps->temp)) < JSON_MAX_STRING_LENGTH / 2) + { + if (rcs_catc ((jsps->temp), c) != RS_OK) + { + return JSON_MEMORY; + } + } + else + { + jsps->string_length_limit_reached = 1; + } + } + jsps->state = 19; /* parse number: fractional part */ + break; + + default: + return JSON_ILLEGAL_CHARACTER; + break; + } + return JSON_OK; + } + +state19: /* parse number: fraccional part */ + { + switch (c) + { + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + if (!jsps->string_length_limit_reached) + { + if (rcs_length ((jsps->temp)) < JSON_MAX_STRING_LENGTH / 2) + { + if (rcs_catc ((jsps->temp), c) != RS_OK) + { + return JSON_MEMORY; + } + } + else + { + jsps->string_length_limit_reached = 1; + } + } +/* jsps->state = 19; // parse number: fractional part*/ + break; + + case 'e': + case 'E': + if (rcs_catc ((jsps->temp), c) != RS_OK) + { + return JSON_MEMORY; + } + + jsps->state = 20; /* parse number: start exponent part */ + break; + + + case '\x20': + case '\x09': + case '\x0A': + case '\x0D': /* JSON insignificant white spaces */ + + if ((jsps->temp) == NULL) + return JSON_MEMORY; + if (jsf->new_number != NULL) + { + jsf->new_number ((jsps->temp)->text); + } + rcs_free (& jsps->temp); + + jsps->state = 0; + break; + + case '}': + + if ((jsps->temp) == NULL) + return JSON_MEMORY; + if (jsf->new_number != NULL) + { + jsf->new_number ((jsps->temp)->text); + } + rcs_free (& jsps->temp); + + if (jsf->open_object != NULL) + jsf->close_object (); + jsps->state = 26; /* close object/array */ + break; + + case ']': + if (jsf->new_number != NULL) + { + if ((jsps->temp) == NULL) + return JSON_MEMORY; + jsf->new_number ((jsps->temp)->text); + rcs_free (& jsps->temp); + } + else + { + rcs_free (& jsps->temp); + jsps->temp = NULL; + } + if (jsf->open_object != NULL) + jsf->close_array (); + jsps->state = 26; /* close object/array */ + break; + + case ',': + + if ((jsps->temp) == NULL) + return JSON_MEMORY; + if (jsf->new_number != NULL) + { + jsf->new_number ((jsps->temp)->text); + } + rcs_free (& jsps->temp); + + if (jsf->label_value_separator != NULL) + jsf->label_value_separator (); + jsps->state = 27; /* sibling followup */ + break; + + + default: + return JSON_ILLEGAL_CHARACTER; + break; + } + return JSON_OK; + } + +state20: /* parse number: start exponent part */ + { + switch (c) + { + case '+': + case '-': + jsps->string_length_limit_reached = 0; + if (rcs_catc ((jsps->temp), c) != RS_OK) + { + return JSON_MEMORY; + } + + jsps->state = 22; /* parse number: exponent sign part */ + break; + + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + if (!jsps->string_length_limit_reached) + { + if (rcs_length ((jsps->temp)) < JSON_MAX_STRING_LENGTH) + { + if (rcs_catc ((jsps->temp), c) != RS_OK) + { + return JSON_MEMORY; + } + + } + else + { + jsps->string_length_limit_reached = 1; + } + } + jsps->state = 21; /* parse number: exponent part */ + break; + + default: + return JSON_ILLEGAL_CHARACTER; + break; + } + return JSON_OK; + } + +state21: /* parse number: exponent part */ + { + switch (c) + { + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + if (!jsps->string_length_limit_reached) + { + if (rcs_length ((jsps->temp)) < JSON_MAX_STRING_LENGTH) + { + if (rcs_catc ((jsps->temp), c) != RS_OK) + { + return JSON_MEMORY; + } + } + else + { + jsps->string_length_limit_reached = 1; + } + } +/* jsps->state = 21; // parse number: exponent part*/ + break; + + case '\x20': + case '\x09': + case '\x0A': + case '\x0D': /* JSON insignificant white spaces */ + + if ((jsps->temp) == NULL) + return JSON_MEMORY; + if (jsf->new_number != NULL) + { + jsf->new_number ((jsps->temp)->text); + } + rcs_free (& jsps->temp); + + jsps->state = 0; + break; + + case '}': + if ((jsps->temp) == NULL) + return JSON_MEMORY; + if (jsf->new_number != NULL) + { + jsf->new_number ((jsps->temp)->text); + } + rcs_free (& jsps->temp); + + if (jsf->open_object != NULL) + jsf->close_object (); + jsps->state = 26; /* close object */ + break; + + case ']': + if (jsf->new_number != NULL) + { + if ((jsps->temp) == NULL) + return JSON_MEMORY; + jsf->new_number ((jsps->temp)->text); + VIR_FREE(jsps->temp); + } + else + { + VIR_FREE(jsps->temp); + jsps->temp = NULL; + } + if (jsf->open_object != NULL) + jsf->close_array (); + jsps->state = 26; /* close object/array */ + break; + + case ',': + if (jsf->new_number != NULL) + { + if ((jsps->temp) == NULL) + return JSON_MEMORY; + jsf->new_number ((jsps->temp)->text); + VIR_FREE(jsps->temp); + } + else + { + free (jsps->temp); + jsps->temp = NULL; + } + if (jsf->label_value_separator != NULL) + jsf->label_value_separator (); + jsps->state = 27; /* sibling followup */ + break; + + default: + return JSON_ILLEGAL_CHARACTER; + break; + } + return JSON_OK; + } + +state22: /* parse number: start exponent part */ + { + switch (c) + { + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + if (!jsps->string_length_limit_reached) + { + if (rcs_length ((jsps->temp)) < JSON_MAX_STRING_LENGTH) + { + rcs_catc ((jsps->temp), c); + } + else + { + jsps->string_length_limit_reached = 1; + } + } + jsps->state = 21; /* parse number: exponent part */ + break; + + default: + return JSON_ILLEGAL_CHARACTER; + break; + } + return JSON_OK; + } + +state23: /* parse number: start negative */ + { + switch (c) + { + case '0': + rcs_catc ((jsps->temp), c); + jsps->state = 17; /* parse number: 0 */ + break; + + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + if (!jsps->string_length_limit_reached) + { + if (rcs_length ((jsps->temp)) < JSON_MAX_STRING_LENGTH / 2) + { + if ((jsps->temp = rcs_create (5)) == NULL) + { + return JSON_MEMORY; + } + if (rcs_catc ((jsps->temp), c) != RS_OK) + { + return JSON_MEMORY; + } + else + { + jsps->string_length_limit_reached = 1; + } + } + } + jsps->state = 24; /* parse number: start decimal part */ + break; + + default: + return JSON_ILLEGAL_CHARACTER; + break; + } + return JSON_OK; + } + +state24: /* parse number: decimal part */ + { + switch (c) + { + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + if (!jsps->string_length_limit_reached) + { + if (rcs_length ((jsps->temp)) < JSON_MAX_STRING_LENGTH / 2) + { + if ((jsps->temp = rcs_create (5)) == NULL) + { + return JSON_MEMORY; + } + if (rcs_catc ((jsps->temp), c) != RS_OK) + { + return JSON_MEMORY; + } + } + else + { + jsps->string_length_limit_reached = 1; + } + } +/* jsps->state = 24; // parse number: decimal part*/ + break; + + case '.': + if ((jsps->temp = rcs_create (5)) == NULL) + { + return JSON_MEMORY; + } + if (rcs_catc ((jsps->temp), '.') != RS_OK) + { + return JSON_MEMORY; + } + + jsps->state = 18; /* parse number: start exponent part */ + break; + + case 'e': + case 'E': + if ((jsps->temp = rcs_create (5)) == NULL) + { + return JSON_MEMORY; + } + if (rcs_catc ((jsps->temp), c) != RS_OK) + { + return JSON_MEMORY; + } + + jsps->string_length_limit_reached = 0; /* reset to accept the exponential part */ + jsps->state = 20; /* parse number: start exponent part */ + break; + + case '\x20': + case '\x09': + case '\x0A': + case '\x0D': /* JSON insignificant white spaces */ + if ((jsps->temp) == NULL) + return JSON_MEMORY; + if (jsf->new_number != NULL) + { + jsf->new_number ((jsps->temp)->text); + } + rcs_free (& jsps->temp); + + jsps->state = 0; + break; + + case '}': + if ((jsps->temp) == NULL) + return JSON_MEMORY; + if (jsf->new_number != NULL) + { + jsf->new_number ((jsps->temp)->text); + } + rcs_free (& jsps->temp); + + if (jsf->open_object != NULL) + jsf->close_object (); + jsps->state = 26; /* close object/array */ + break; + + case ']': + if ((jsps->temp) == NULL) + return JSON_MEMORY; + if (jsf->new_number != NULL) + { + jsf->new_number ((jsps->temp)->text); + } + rcs_free (& jsps->temp); + + if (jsf->open_object != NULL) + jsf->close_array (); + jsps->state = 26; /* close object/array */ + break; + + case ',': + if ((jsps->temp) == NULL) + return JSON_MEMORY; + if (jsf->new_number != NULL) + { + jsf->new_number ((jsps->temp)->text); + } + rcs_free (& jsps->temp); + + if (jsf->label_value_separator != NULL) + jsf->label_value_separator (); + jsps->state = 27; /* sibling followup */ + break; + + default: + return JSON_ILLEGAL_CHARACTER; + break; + } + return JSON_OK; + } + +state25: /* open object */ + { + switch (c) + { + case '\x20': + case '\x09': + case '\x0A': + case '\x0D': /* JSON insignificant white spaces */ + break; + + case '\"': + jsps->temp = NULL; + jsps->state = 1; + break; + + case '}': + if (jsf->close_object != NULL) + jsf->close_object (); + jsps->state = 26; /* close object */ + break; + + default: + return JSON_ILLEGAL_CHARACTER; + break; + } + return JSON_OK; + } + +state26: /* close object/array */ + { + switch (c) + { + case '\x20': + case '\x09': + case '\x0A': + case '\x0D': /* JSON insignificant white spaces */ + break; + + case '}': + if (jsf->close_object != NULL) + jsf->close_object (); +/* jsp->state = 26; // close object*/ + break; + + case ']': + if (jsf->close_array != NULL) + jsf->close_array (); +/* jsps->state = 26; // close object/array*/ + break; + + case ',': + if (jsf->sibling_separator != NULL) + jsf->sibling_separator (); + jsps->state = 27; /* sibling followup */ + break; + + default: + return JSON_ILLEGAL_CHARACTER; + break; + } + return JSON_OK; + } + +state27: /* sibling followup */ + { + switch (c) + { + case '\x20': + case '\x09': + case '\x0A': + case '\x0D': /* JSON insignificant white spaces */ + break; + + case '\"': + jsps->state = 1; + jsps->temp = NULL; + break; + + case '{': + if (jsf->open_object != NULL) + jsf->open_object (); + jsps->state = 25; /*open object */ + break; + + case '[': + if (jsf->open_array != NULL) + jsf->open_array (); +/* jsps->state = 0; // redundant*/ + break; + + case 't': + jsps->state = 7; /* parse true: tr */ + break; + + case 'f': + jsps->state = 10; /* parse false: fa */ + break; + + case 'n': + jsps->state = 14; /* parse null: nu */ + break; + + case '0': + jsps->state = 17; /* parse number: 0 */ + if ((jsps->temp = rcs_create (5)) == NULL) + { + return JSON_MEMORY; + } + if (rcs_catc ((jsps->temp), '0') != RS_OK) + { + return JSON_MEMORY; + } + break; + + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + jsps->state = 24; /* parse number: decimal */ + if ((jsps->temp = rcs_create (5)) == NULL) + { + return JSON_MEMORY; + } + if (rcs_catc ((jsps->temp), c) != RS_OK) + { + return JSON_MEMORY; + } + break; + + case '-': + jsps->state = 23; /* number: */ + if ((jsps->temp = rcs_create (RSTRING_DEFAULT)) == NULL) + { + return JSON_MEMORY; + } + if (rcs_catc ((jsps->temp), '-') != RS_OK) + { + return JSON_MEMORY; + } + break; + + default: + return JSON_ILLEGAL_CHARACTER; + break; + } + return JSON_OK; + } + + return JSON_UNKNOWN_PROBLEM; +} + + +enum json_error +json_find_first_label (const json_t * object, const char *text_label, json_t **value) +{ + json_t *cursor; + + for (cursor = object->child; cursor != NULL; cursor = cursor->next) + { + if (STREQ(cursor->text, text_label)) { + *value = cursor; + return JSON_OK; + } + } + *value = NULL; + return JSON_UNKNOWN_PROBLEM; +} diff --git a/src/util/json.h b/src/util/json.h new file mode 100644 index 0000000..42dbcf7 --- /dev/null +++ b/src/util/json.h @@ -0,0 +1,313 @@ +/*************************************************************************** + * Copyright (C) 2007 by Rui Maciel * + * rui.maciel@gmail.com * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU Library General Public License as * + * published by the Free Software Foundation; either version 2 of the * + * License, or (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU Library General Public * + * License along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ + +/** @file json.h A small library that helps deal with JSON-encoded information + \ingroup JSON + + \note error handling is only in a very rudimentary form. + \author Rui Maciel rui_maciel@users.sourceforge.net + \version v1.1 +*/ + +#include <stdint.h> +#include <stdio.h> + +#ifndef JSON_H +#define JSON_H + +#define JSON_MAX_STRING_LENGTH SIZE_MAX-1 + +struct rui_cstring +{ + char *text; /*<! char c-string */ + size_t length; /*<! put in place to avoid strlen() calls */ + size_t max; /*<! usable memory allocated to text minus the space for the nul character */ +}; + +typedef struct rui_cstring rcstring; + +/** + The descriptions of the json_value node type +**/ + enum json_value_type + { JSON_STRING = 0, JSON_NUMBER, JSON_OBJECT, JSON_ARRAY, JSON_TRUE, JSON_FALSE, JSON_NULL }; + +/** + The error messages produced by the JSON parsers +**/ + enum json_error + { + JSON_OK = 1, /*!< everything went smoothly */ + JSON_INCOMPLETE_DOCUMENT, /*!< the parsed document didn't ended */ + JSON_WAITING_FOR_EOF, /*!< A complete JSON document tree was already finished but needs to get to EOF. Other characters beyond whitespaces produce errors */ + JSON_MALFORMED_DOCUMENT, /* the JSON document which was fed to this parser is malformed */ + JSON_INCOMPATIBLE_TYPE, /*!< the currently parsed type does not belong here */ + JSON_MEMORY, /*!< an error occurred when allocating memory */ + JSON_ILLEGAL_CHARACTER, /*!< the currently parsed character does not belong here */ + JSON_BAD_TREE_STRUCTURE, /*!< the document tree structure is malformed */ + JSON_MAXIMUM_LENGTH, /*!< the parsed string reached the maximum allowed size */ + JSON_UNKNOWN_PROBLEM /*!< some random, unaccounted problem occurred */ + }; + + +/** + The JSON document tree node, which is a basic JSON type +**/ + typedef struct json_value + { + enum json_value_type type; /*!< the type of node */ + char *text; /*!< The text stored by the node. It stores UTF-8 strings and is used exclusively by the JSON_STRING and JSON_NUMBER node types */ + + /* FIFO queue data */ + struct json_value *next; /*!< The pointer pointing to the next element in the FIFO sibling list */ + struct json_value *previous; /*!< The pointer pointing to the previous element in the FIFO sibling list */ + struct json_value *parent; /*!< The pointer pointing to the parent node in the document tree */ + struct json_value *child; /*!< The pointer pointing to the first child node in the document tree */ + struct json_value *child_end; /*!< The pointer pointing to the last child node in the document tree */ + } json_t; + + +/** + The structure holding all information needed to resume parsing +**/ + struct json_parsing_info + { + unsigned int state; /*!< the state where the parsing was left on the last parser run */ + unsigned int lex_state; + rcstring *lex_text; + const char *p; + int string_length_limit_reached; /*!< flag informing if the string limit length defined by JSON_MAX_STRING_LENGTH was reached */ + json_t *cursor; /*!< pointers to nodes belonging to the document tree which aid the document parsing */ + }; + + +/** + The structure which holds the pointers to the functions that will be called by the saxy parser whenever their evens are triggered +**/ + struct json_saxy_functions + { + int (*open_object) (void); + int (*close_object) (void); + int (*open_array) (void); + int (*close_array) (void); + int (*new_string) (char *text); + int (*new_number) (char *text); + int (*new_true) (void); + int (*new_false) (void); + int (*new_null) (void); + int (*label_value_separator) (void); + int (*sibling_separator) (void); + }; + + +/** + The structure holding the information needed for json_saxy_parse to resume parsing +**/ + struct json_saxy_parser_status + { + unsigned int state; /*!< current parser state */ + int string_length_limit_reached; /*!< flag informing if the string limit length defined by JSON_MAX_STRING_LENGTH was reached */ + rcstring *temp; /*!< temporary string which will be used to build up parsed strings between parser runs. */ + }; + + +/** + Buils a json_t document by parsing an open file + @param file a pointer to an object controlling a stream, returned by fopen() + @param document a reference to a json_t pointer, set to NULL, which will store the parsed document + @return a json_error error code according to how the parsing operation went. +**/ + enum json_error json_stream_parse (FILE * file, json_t ** document); + + +/** + Creates a new JSON value and defines it's type + @param type the value's type + @return a pointer to the newly created value structure +**/ + json_t *json_new_value (const enum json_value_type type); + + +/** + Creates a new JSON string and defines it's text + @param text the value's text + @return a pointer to the newly created JSON string value +**/ + json_t *json_new_string (const char *text); + + +/** + Creates a new JSON number and defines it's text. The user is responsible for the number string's correctness + @param text the value's number + @return a pointer to the newly created JSON string value +**/ + json_t *json_new_number (const char *text); + + +/** + Creates a new JSON object + @return a pointer to the newly created JSON object value +**/ + json_t *json_new_object (void); + + +/** + Creates a new JSON array + @return a pointer to the newly created JSON array value +**/ + json_t *json_new_array (void); + + +/** + Creates a new JSON null + @return a pointer to the newly created JSON null value +**/ + json_t *json_new_null (void); + + +/** + Creates a new JSON true + @return a pointer to the newly created JSON true value +**/ + json_t *json_new_true (void); + + +/** + Creates a new JSON false + @return a pointer to the newly created JSON false value +**/ + json_t *json_new_false (void); + + +/** + Frees the memory appointed to the value fed as the parameter, as well as all the child nodes + @param value the root node of the tree being freed +**/ + void json_free_value (json_t ** value); + + +/** + Inserts a child node into a parent node, as well as performs some document tree integrity checks. + @param parent the parent node + @param child the node being added as a child to parent + @return the error code corresponding to the operation result +**/ + enum json_error json_insert_child (json_t * parent, json_t * child); + + +/** + Inserts a label:value pair into a parent node, as well as performs some document tree integrity checks. + @param parent the parent node + @param text_label a char string which serves as the label in the label:value pair + @param value the value in the label:value pair + @return the error code corresponding to the operation result +**/ + enum json_error json_insert_pair_into_object (json_t * parent, const char *text_label, json_t * value); + + enum json_error json_insert_string_pair_into_object (json_t * parent, const char *text_label, const char *value); + + +/** + Produces a JSON markup text document from a document tree + @param root The document's root node + @param text a pointer to a char string that will hold the JSON document text. + @return a json_error code describing how the operation went +**/ + enum json_error json_tree_to_string (json_t * root, char **text); + + +/** + Produces a JSON markup text document from a json_t document tree to a text stream + @param file a opened file stream + @param root The document's root node + @return a json_error code describing how the operation went +**/ + enum json_error json_stream_output (FILE * file, json_t * root); + + +/** + Strips all JSON white spaces from the text string + @param text a char string holding a JSON document or document snippet +**/ + void json_strip_white_spaces (char *text); + + +/** + Formats a JSON markup text contained in the given string + @param text a JSON formatted document + @return a pointer to a char string holding the formated document +**/ + char *json_format_string (const char *text); + + +/** + Outputs a new UTF8 c-string which replaces all characters that must be escaped with their respective escaped versions + @param text an UTF8 char text string + @return an UTF-8 c-string holding the same text string but with escaped characters +**/ + char *json_escape (char *text); + + +/** + This function takes care of the tedious task of initializing any instance of + struct json_parsing_info + @param jpi a pointer to a struct json_parsing_info instance +**/ + void json_jpi_init (struct json_parsing_info *jpi); + + +/** + Produces a document tree sequentially from a JSON markup text fragment + @param info the information necessary to resume parsing any incomplete document + @param buffer a null-terminated c-string containing a JSON document fragment + @return a code describing how the operation ended up +**/ + enum json_error json_parse_fragment (struct json_parsing_info *info, const char *buffer); + + +/** + Produces a document tree from a JSON markup text string that contains a complete document + @param root a reference to a pointer to a json_t type. The function allocates memory to the passed pointer and sets up the value + @param text a c-string containing a complete JSON text document + @return a pointer to the new document tree or NULL if some error occurred +**/ + enum json_error json_parse_document (json_t ** root, const char *text); + + +/** + Function to perform a SAX-like parsing of any JSON document or document fragment that is passed to it + @param jsps a structure holding the status information of the current parser + @param jsf a structure holding the function pointers to the event functions + @param c the character to be parsed + @return a json_error code informing how the parsing went +**/ + enum json_error json_saxy_parse (struct json_saxy_parser_status *jsps, struct json_saxy_functions *jsf, char c); + + +/** + Searches through the object's children for a label holding the text text_label + @param object a json_value of type JSON_OBJECT + @param text_label the c-string to search for through the object's child labels + @return a pointer to the first label holding a text equal to text_label or NULL if there is no such label or if object has no children +**/ + enum json_error json_find_first_label (const json_t * object, const char *text_label, json_t **value); + + +#endif -- 1.6.2.5

On Tue, Nov 03, 2009 at 02:50:08PM -0500, Daniel P. Berrange wrote:
Import JSON parsing / formatting helper code based on code from
Hum http://mjson.sourceforge.net/ -> news: MJSON v0.9 released http://sourceforge.net/projects/mjson/ -> Download now mjson-1.2.tar.gz what version did you based this from ?
with some API changes to better cope with libvirt's needs.
I notice no error or debug is emitted directly from this module.
+static int +lexer (const char *buffer, const char **p, unsigned int *state, rcstring ** text) +{ + if (*p == NULL) + *p = buffer; + + while (**p != '\0') + { + switch (*state) + { + + case 0: /* Root document */ + { + switch (*(*p)++) + { + case '\x20': /* space */ + case '\x09': /* horizontal tab */ + case '\x0A': /* line feed or new line */ + case '\x0D': /* Carriage return */ + break; + + case '{': + return LEX_BEGIN_OBJECT; + case '}': + return LEX_END_OBJECT; + case '[': + return LEX_BEGIN_ARRAY; + case ']': + return LEX_END_ARRAY; + case ':': + return LEX_NAME_SEPARATOR; + case ',': + return LEX_VALUE_SEPARATOR; + + case '\"': + *text = rcs_create (RSTRING_DEFAULT); + if (*text == NULL) + return LEX_MEMORY; + *state = 1; /* inside a JSON string */ + break; + + case 't': + *state = 7; /* true: 1 */ + break; + + case 'f': + *state = 10; /* false: 1 */ + break; + + case 'n': + *state = 14; /* false: 1 */ + break; + + case '-': + *text = rcs_create (RSTRING_DEFAULT); + if (*text == NULL) + return LEX_MEMORY; + if (rcs_catc (*text, '-') != RS_OK) + return LEX_MEMORY; + *state = 17; /* number: '0' */ + break; + + case '0': + *text = rcs_create (RSTRING_DEFAULT); + if (*text == NULL) + return LEX_MEMORY; + if (rcs_catc (*text, '0') != RS_OK) + return LEX_MEMORY; + *state = 18; /* number: '0' */ + break; + + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + *text = rcs_create (RSTRING_DEFAULT); + if (*text == NULL) + return LEX_MEMORY; + if (rcs_catc (*text, *(*p - 1)) != RS_OK) + return LEX_MEMORY; + *state = 19; /* number: decimal followup */ + break; + + + default: + return LEX_INVALID_CHARACTER; + } + } + break; + + case 1: /* inside a JSON string */ + { + switch (**p) + { + case 1: + case 2: + case 3: + case 4: + case 5: + case 6: + case 7: + case 8: + case 9: + case 10: /* line feed */ + case 11: + case 12: + case 13: /* carriage return */ + case 14: + case 15: + case 16: + case 17: + case 18: + case 19: + case 20: + case 21: + case 22: + case 23: + case 24: + case 25: + case 26: + case 27: + case 28: + case 29: + case 30: + case 31: + /* ASCII control characters can only be present in a JSON string if they are escaped. If not then the document is invalid */ + return LEX_INVALID_CHARACTER; + break; + + case '\"': /* close JSON string */ + /* it is expected that, in the routine that calls this function, text is set to NULL */ + *state = 0; + ++*p; + return LEX_STRING; + break; + + case '\\': + if (rcs_catc (*text, '\\') != RS_OK) + return LEX_MEMORY; + *state = 2; /* inside a JSON string: start escape sequence */ + break; + + default: + if (rcs_catc (*text, **p) != RS_OK) + return LEX_MEMORY; + } + ++*p; + } + break; + + case 2: /* inside a JSON string: start escape sequence */ + { + switch (**p) + { + case '\\': + case '\"': + case '/': + case 'b': + case 'f': + case 'n': + case 'r': + case 't': + if (rcs_catc (*text, **p) != RS_OK) + return LEX_MEMORY; + *state = 1; /* inside a JSON string */ + break; + + case 'u': + if (rcs_catc (*text, **p) != RS_OK) + return LEX_MEMORY; + *state = 3; /* inside a JSON string: escape unicode */ + break; + + default: + return LEX_INVALID_CHARACTER; + } + ++*p; + } + break; + + case 3: /*inside a JSON string: escape unicode */ + { + if ((**p >= 'a') && (**p <= 'f')) + { + if (rcs_catc (*text, **p) != RS_OK) + return LEX_MEMORY; + *state = 4; /* inside a JSON string: escape unicode */ + } + else if ((**p >= 'A') && (**p <= 'F')) + { + if (rcs_catc (*text, **p) != RS_OK) + return LEX_MEMORY; + *state = 4; /* inside a JSON string: escape unicode */ + } + else if ((**p >= '0') && (**p <= '9')) + { + if (rcs_catc (*text, **p) != RS_OK) + return LEX_MEMORY; + *state = 4; /* inside a JSON string: escape unicode */ + } + else + return LEX_INVALID_CHARACTER; + ++*p; + } + break; + + case 4: /* inside a JSON string: escape unicode */ + { + if ((**p >= 'a') && (**p <= 'f')) + { + if (rcs_catc (*text, **p) != RS_OK) + return LEX_MEMORY; + *state = 5; /* inside a JSON string: escape unicode */ + } + else if ((**p >= 'A') && (**p <= 'F')) + { + if (rcs_catc (*text, **p) != RS_OK) + return LEX_MEMORY; + *state = 5; /* inside a JSON string: escape unicode */ + } + else if ((**p >= '0') && (**p <= '9')) + { + if (rcs_catc (*text, **p) != RS_OK) + return LEX_MEMORY; + *state = 5; /* inside a JSON string: escape unicode */ + } + else + return LEX_INVALID_CHARACTER; + ++*p; + } + + case 5: /* inside a JSON string: escape unicode */ + { + if ((**p >= 'a') && (**p <= 'f')) + { + if (rcs_catc (*text, **p) != RS_OK) + return LEX_MEMORY; + *state = 6; /* inside a JSON string: escape unicode */ + } + else if ((**p >= 'A') && (**p <= 'F')) + { + if (rcs_catc (*text, **p) != RS_OK) + return LEX_MEMORY; + *state = 6; /* inside a JSON string: escape unicode */ + } + else if ((**p >= '0') && (**p <= '9')) + { + if (rcs_catc (*text, **p) != RS_OK) + return LEX_MEMORY; + *state = 6; /* inside a JSON string: escape unicode */ + } + else + return LEX_INVALID_CHARACTER; + ++*p; + } + break; + + case 6: /* inside a JSON string: escape unicode */ + { + if ((**p >= 'a') && (**p <= 'f')) + { + if (rcs_catc (*text, **p) != RS_OK) + return LEX_MEMORY; + *state = 1; /* inside a JSON string: escape unicode */ + } + else if ((**p >= 'A') && (**p <= 'F')) + { + if (rcs_catc (*text, **p) != RS_OK) + return LEX_MEMORY; + *state = 1; /* inside a JSON string: escape unicode */ + } + else if ((**p >= '0') && (**p <= '9')) + { + if (rcs_catc (*text, **p) != RS_OK) + return LEX_MEMORY; + *state = 1; /* inside a JSON string: escape unicode */ + } + else + return LEX_INVALID_CHARACTER; + ++*p; + } + break; + + case 7: /* true: 1 */ + { + switch (*(*p)++) + { + case 'r': + *state = 8; + break; + default: + return LEX_INVALID_CHARACTER; + break; + } + } + break; + + case 8: /* true: 2 */ + { + switch (*(*p)++) + { + case 'u': + *state = 9; + break; + default: + return LEX_INVALID_CHARACTER; + break; + } + } + break; + + case 9: /* true: 3 */ + { + switch (*(*p)++) + { + case 'e': + *state = 0; + return LEX_TRUE; + break; + default: + return LEX_INVALID_CHARACTER; + break; + } + } + break; + + case 10: /* false: 1 */ + { + switch (*(*p)++) + { + case 'a': + *state = 11; + break; + default: + return LEX_INVALID_CHARACTER; + break; + } + } + break; + + case 11: /* false: 2 */ + { + switch (*(*p)++) + { + case 'l': + *state = 12; + break; + default: + return LEX_INVALID_CHARACTER; + break; + } + } + break; + + case 12: /* false: 3 */ + { + switch (*(*p)++) + { + case 's': + *state = 13; + break; + default: + return LEX_INVALID_CHARACTER; + break; + } + } + break; + + case 13: /* false: 4 */ + { + switch (*(*p)++) + { + case 'e': + *state = 0; + return LEX_FALSE; + break; + default: + return LEX_INVALID_CHARACTER; + break; + } + } + break; + + case 14: /* null: 1 */ + { + switch (*(*p)++) + { + case 'u': + *state = 15; + break; + default: + return LEX_INVALID_CHARACTER; + break; + } + } + break; + + case 15: /* null: 2 */ + { + switch (*(*p)++) + { + case 'l': + *state = 16; + break; + default: + return LEX_INVALID_CHARACTER; + break; + } + } + break; + + case 16: /* null: 3 */ + { + switch (*(*p)++) + { + case 'l': + *state = 0; + return LEX_NULL; + break; + default: + return LEX_INVALID_CHARACTER; + break; + } + } + break; + + case 17: /* number: minus sign */ + { + switch (**p) + { + case '0': + if (rcs_catc (*text, **p) != RS_OK) + return LEX_MEMORY; + ++*p; + *state = 18; /* number: '0' */ + break; + + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + if (rcs_catc (*text, **p) != RS_OK) + return LEX_MEMORY; + ++*p; + *state = 19; /* number: decimal followup */ + break; + + default: + return LEX_INVALID_CHARACTER; + break; + } + } + break; + + case 18: /* number: '0' */ + { + switch (**p) + { + case '\x20': /* space */ + case '\x09': /* horizontal tab */ + case '\x0A': /* line feed or new line */ + case '\x0D': /* Carriage return */ + ++*p; + case ']': + case '}': + case ',': + *state = 0; + return LEX_NUMBER; + break; + + case '.': + if (rcs_catc (*text, **p) != RS_OK) + return LEX_MEMORY; + ++*p; + *state = 20; /* number: frac start */ + break; + + case 'e': + case 'E': + if (rcs_catc (*text, **p) != RS_OK) + return LEX_MEMORY; + ++*p; + *state = 22; /* number: exp start */ + break; + + default: + return LEX_INVALID_CHARACTER; + break; + } + } + break; + + case 19: /* number: int followup */ + { + switch (**p) + { + case '\x20': /* space */ + case '\x09': /* horizontal tab */ + case '\x0A': /* line feed or new line */ + case '\x0D': /* Carriage return */ + ++*p; + case ']': + case '}': + case ',': + *state = 0; + return LEX_NUMBER; + break; + + case '.': + if (rcs_catc (*text, **p) != RS_OK) + return LEX_MEMORY; + ++*p; + *state = 20; /* number: frac start */ + break; + + case 'e': + case 'E': + if (rcs_catc (*text, **p) != RS_OK) + return LEX_MEMORY; + ++*p; + *state = 22; /* number: exp start */ + break; + + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + if (rcs_catc (*text, **p) != RS_OK) + return LEX_MEMORY; + ++*p; + break; + + default: + return LEX_INVALID_CHARACTER; + break; + } + } + break; + + case 20: /* number: frac start */ + { + switch (**p) + { + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + if (rcs_catc (*text, **p) != RS_OK) + return LEX_MEMORY; + ++*p; + *state = 21; /* number: frac continue */ + break; + + default: + return LEX_INVALID_CHARACTER; + break; + } + } + break; + + case 21: /* number: frac continue */ + { + switch (**p) + { + case '\x20': /* space */ + case '\x09': /* horizontal tab */ + case '\x0A': /* line feed or new line */ + case '\x0D': /* Carriage return */ + ++*p; + case ']': + case '}': + case ',': + *state = 0; + return LEX_NUMBER; + break; + + case 'e': + case 'E': + if (rcs_catc (*text, **p) != RS_OK) + return LEX_MEMORY; + ++*p; + *state = 22; /* number: exp start */ + break; + + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + if (rcs_catc (*text, **p) != RS_OK) + return LEX_MEMORY; + ++*p; + break; + + default: + return LEX_INVALID_CHARACTER; + break; + } + } + break; + + case 22: /* number: exp start */ + { + switch (**p) + { + case '-': + case '+': + if (rcs_catc (*text, **p) != RS_OK) + return LEX_MEMORY; + ++*p; + *state = 23; /* number: exp continue */ + break; + + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + if (rcs_catc (*text, **p) != RS_OK) + return LEX_MEMORY; + ++*p; + *state = 24; /* number: exp end */ + break; + + default: + return LEX_INVALID_CHARACTER; + break; + } + } + break; + + case 23: /* number: exp continue */ + { + switch (**p) + { + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + if (rcs_catc (*text, **p) != RS_OK) + return LEX_MEMORY; + ++*p; + *state = 24; /* number: exp end */ + break; + + default: + return LEX_INVALID_CHARACTER; + break; + } + } + break; + + case 24: /* number: exp end */ + { + switch (**p) + { + case '\x20': /* space */ + case '\x09': /* horizontal tab */ + case '\x0A': /* line feed or new line */ + case '\x0D': /* Carriage return */ + ++*p; + case ']': + case '}': + case ',': + *state = 0; + return LEX_NUMBER; + break; + + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + if (rcs_catc (*text, **p) != RS_OK) + return LEX_MEMORY; + ++*p; + break; + + default: + return LEX_INVALID_CHARACTER; + break; + } + } + break; + + default: + printf ("*state missing: %d\n", *state); + return LEX_INVALID_CHARACTER; + } + + } + + *p = NULL; + return LEX_MORE; +}
IMHO this is an abomination, but as long as nobody need to fix that code ! Just out of curiosity, what encoding are those character supposed to be into ? It's being checked against ASCII values and there are hints of 'unicode escaping' it's weird.
+enum json_error +json_parse_fragment (struct json_parsing_info *info, const char *buffer) +{ + json_t *temp = NULL; + + info->p = buffer; + while (*info->p != '\0') + { + switch (info->state) + { + case 0: /* starting point */ + { + switch (lexer (buffer, &info->p, &info->lex_state, & info->lex_text)) + { + case LEX_BEGIN_OBJECT: + info->state = 1; /* begin object */ + break; + + case LEX_BEGIN_ARRAY: + info->state = 7; /* begin array */ + break; + + case LEX_INVALID_CHARACTER: + return JSON_MALFORMED_DOCUMENT; + break; + + default: + printf ("state %d: defaulted\n", info->state); + return JSON_MALFORMED_DOCUMENT; + break; + } + } + break; + + case 1: /* open object */ + { + if (info->cursor == NULL) + { + if ((info->cursor = json_new_object ()) == NULL) + { + return JSON_MEMORY; + } + } + else + { + if ((temp = json_new_object ()) == NULL) + { + return JSON_MEMORY; + } + if (json_insert_child (info->cursor, temp) != JSON_OK) + { + return JSON_UNKNOWN_PROBLEM; + } + info->cursor = temp; + temp = NULL; + } + info->state = 2; /* just entered an object */ + } + break; + + case 2: /* opened object */ + { + switch (lexer (buffer, &info->p, &info->lex_state, & info->lex_text)) + { + case LEX_STRING: + if ((temp = json_new_value (JSON_STRING)) == NULL) + return JSON_MEMORY; + temp->text = rcs_unwrap (info->lex_text), info->lex_text = NULL; + if (json_insert_child (info->cursor, temp) != JSON_OK) + { + /*TODO return value according to the value returned from json_insert_child() */ + return JSON_UNKNOWN_PROBLEM; + } + info->cursor = temp; + temp = NULL; + info->state = 5; /* label, pre label:value separator */ + break; + + case LEX_END_OBJECT: + if (info->cursor->parent == NULL) + { + info->state = 99; /* finished document. only accept whitespaces until EOF */ + } + else + { + info->cursor = info->cursor->parent; + switch (info->cursor->type) + { + case JSON_STRING: + info->cursor = info->cursor->parent; + if (info->cursor->type != JSON_OBJECT) + { + return JSON_BAD_TREE_STRUCTURE; + } + else + { + info->state = 3; /* finished adding a field to an object */ + } + break; + + case JSON_ARRAY: + info->state = 9; + break; + + default: + return JSON_BAD_TREE_STRUCTURE; + } + } + break; + + case LEX_MORE: + return JSON_INCOMPLETE_DOCUMENT; + break; + + default: + printf ("state %d: defaulted\n", info->state); + return JSON_MALFORMED_DOCUMENT; + break; + } + } + break; + + case 3: /* finished adding a field to an object */ + { + switch (lexer (buffer, &info->p, &info->lex_state, & info->lex_text)) + { + case LEX_VALUE_SEPARATOR: + info->state = 4; /* sibling, post-object */ + break; + + case LEX_END_OBJECT: + if (info->cursor->parent == NULL) + { + info->state = 99; /* parse until EOF */ + } + else + { + info->cursor = info->cursor->parent; + switch (info->cursor->type) + { + case JSON_STRING: + info->cursor = info->cursor->parent; + if (info->cursor->type != JSON_OBJECT) + { + return JSON_BAD_TREE_STRUCTURE; + } + else + { + info->state = 3; /* finished adding a field to an object */ + } + break; + + case JSON_ARRAY: + info->state = 9; + break; + + default: + return JSON_BAD_TREE_STRUCTURE; + } + } + break; + + case LEX_MORE: + return JSON_INCOMPLETE_DOCUMENT; + break; + + default: + printf ("state %d: defaulted\n", info->state); + return JSON_MALFORMED_DOCUMENT; + break; + } + } + break; + + case 4: /* sibling, post-object */ + { + switch (lexer (buffer, &info->p, &info->lex_state, & info->lex_text)) + { + case LEX_STRING: + if ((temp = json_new_value (JSON_STRING)) == NULL) + return JSON_MEMORY; + temp->text = rcs_unwrap (info->lex_text), info->lex_text = NULL; + if (json_insert_child (info->cursor, temp) != JSON_OK) + { + return JSON_UNKNOWN_PROBLEM; + } + info->cursor = temp; + temp = NULL; + info->state = 5; + break; + + case LEX_MORE: + return JSON_INCOMPLETE_DOCUMENT; + break; + + case LEX_INVALID_CHARACTER: + return JSON_ILLEGAL_CHARACTER; + break; + + default: + printf ("state %d: defaulted\n", info->state); + return JSON_MALFORMED_DOCUMENT; + break; + } + } + break; + + case 5: /* label, pre name separator */ + { + switch (lexer (buffer, &info->p, &info->lex_state, & info->lex_text)) + { + case LEX_NAME_SEPARATOR: + info->state = 6; /* label, pos label:value separator */ + break; + + case LEX_MORE: + return JSON_INCOMPLETE_DOCUMENT; + break; + + default: + printf ("state %d: defaulted\n", info->state); + return JSON_MALFORMED_DOCUMENT; + break; + } + } + break; + + case 6: /* label, pos name separator */ + { + unsigned int value; /* to avoid redundant code */ + + switch (value = lexer (buffer, &info->p, &info->lex_state, & info->lex_text)) + { + case LEX_STRING: + if ((temp = json_new_value (JSON_STRING)) == NULL) + return JSON_MEMORY; + temp->text = rcs_unwrap (info->lex_text), info->lex_text = NULL; + if (json_insert_child (info->cursor, temp) != JSON_OK) + { + /*TODO specify the exact error message */ + return JSON_UNKNOWN_PROBLEM; + } + if (info->cursor->parent == NULL) + { + info->state = 99; /* finished document. only accepts whitespaces until EOF */ + } + else + { + info->cursor = info->cursor->parent; + } + temp = NULL; + info->state = 3; /* finished adding a field to an object */ + break; + + case LEX_NUMBER: + if ((temp = json_new_value (JSON_NUMBER)) == NULL) + return JSON_MEMORY; + temp->text = rcs_unwrap (info->lex_text), info->lex_text = NULL; + if (json_insert_child (info->cursor, temp) != JSON_OK) + { + /*TODO specify the exact error message */ + return JSON_UNKNOWN_PROBLEM; + } + if (info->cursor->parent == NULL) + { + info->state = 99; /* finished document. only accepts whitespaces until EOF */ + } + else + { + info->cursor = info->cursor->parent; + } + temp = NULL; + info->state = 3; /* finished adding a field to an object */ + break; + + case LEX_TRUE: + if ((temp = json_new_value (JSON_TRUE)) == NULL) + return JSON_MEMORY; + if (json_insert_child (info->cursor, temp) != JSON_OK) + { + /*TODO specify the exact error message */ + return JSON_UNKNOWN_PROBLEM; + } + if (info->cursor->parent == NULL) + { + info->state = 99; /* finished document. only accepts whitespaces until EOF */ + } + else + { + info->cursor = info->cursor->parent; + } + temp = NULL; + info->state = 3; /* finished adding a field to an object */ + break; + + case LEX_FALSE: + if ((temp = json_new_value (JSON_FALSE)) == NULL) + return JSON_MEMORY; + if (json_insert_child (info->cursor, temp) != JSON_OK) + { + /*TODO specify the exact error message */ + return JSON_UNKNOWN_PROBLEM; + } + if (info->cursor->parent == NULL) + { + info->state = 99; /* finished document. only accepts whitespaces until EOF */ + } + else + { + info->cursor = info->cursor->parent; + } + temp = NULL; + info->state = 3; /* finished adding a field to an object */ + break; + + case LEX_NULL: + if ((temp = json_new_value (JSON_NULL)) == NULL) + return JSON_MEMORY; + if (json_insert_child (info->cursor, temp) != JSON_OK) + { + /*TODO specify the exact error message */ + return JSON_UNKNOWN_PROBLEM; + } + if (info->cursor->parent == NULL) + { + info->state = 99; /* finished document. only accepts whitespaces until EOF */ + } + else + { + info->cursor = info->cursor->parent; + } + temp = NULL; + info->state = 3; /* finished adding a field to an object */ + break; + + case LEX_BEGIN_OBJECT: + info->state = 1; + break; + + case LEX_BEGIN_ARRAY: + info->state = 7; + break; + + case LEX_MORE: + return JSON_INCOMPLETE_DOCUMENT; + break; + + case LEX_MEMORY: + return JSON_MEMORY; + break; + + case LEX_INVALID_CHARACTER: + return JSON_ILLEGAL_CHARACTER; + break; + + default: + printf ("state %d: defaulted\n", info->state); + return JSON_MALFORMED_DOCUMENT; + break; + } + } + break; + + case 7: /* open array */ + { + if (info->cursor == NULL) + { + if ((info->cursor = json_new_array ()) == NULL) + { + return JSON_MEMORY; + } + } + else + { + if ((temp = json_new_array ()) == NULL) + { + return JSON_MEMORY; + } + if (json_insert_child (info->cursor, temp) != JSON_OK) + { + return JSON_UNKNOWN_PROBLEM; + } + info->cursor = temp; + temp = NULL; + } + info->state = 8; /* just entered an array */ + } + break; + + case 8: /* just entered an array */ + { + switch (lexer (buffer, &info->p, &info->lex_state, & info->lex_text)) + { + case LEX_STRING: + if ((temp = json_new_value (JSON_STRING)) == NULL) + return JSON_MEMORY; + temp->text = rcs_unwrap (info->lex_text), info->lex_text = NULL; + if (json_insert_child (info->cursor, temp) != JSON_OK) + { + return JSON_UNKNOWN_PROBLEM; + } + temp = NULL; + info->state = 9; /* label, pre label:value separator */ + break; + + case LEX_NUMBER: + if ((temp = json_new_value (JSON_NUMBER)) == NULL) + return JSON_MEMORY; + temp->text = rcs_unwrap (info->lex_text), info->lex_text = NULL; + if (json_insert_child (info->cursor, temp) != JSON_OK) + { + return JSON_UNKNOWN_PROBLEM; + } + temp = NULL; + info->state = 9; /* label, pre label:value separator */ + break; + + case LEX_TRUE: + if ((temp = json_new_value (JSON_TRUE)) == NULL) + return JSON_MEMORY; + if (json_insert_child (info->cursor, temp) != JSON_OK) + { + return JSON_UNKNOWN_PROBLEM; + } + info->state = 9; /* label, pre label:value separator */ + break; + + case LEX_FALSE: + if ((temp = json_new_value (JSON_FALSE)) == NULL) + return JSON_MEMORY; + if (json_insert_child (info->cursor, temp) != JSON_OK) + { + return JSON_UNKNOWN_PROBLEM; + } + info->state = 9; /* label, pre label:value separator */ + break; + + case LEX_NULL: + if ((temp = json_new_value (JSON_NULL)) == NULL) + return JSON_MEMORY; + if (json_insert_child (info->cursor, temp) != JSON_OK) + { + return JSON_UNKNOWN_PROBLEM; + } + info->state = 9; /* label, pre label:value separator */ + break; + + case LEX_BEGIN_ARRAY: + info->state = 7; /* open array */ + break; + + case LEX_END_ARRAY: + if (info->cursor->parent == NULL) + { + /*TODO implement this */ + info->state = 99; /* finished document. only accept whitespaces until EOF */ + } + else + { + info->cursor = info->cursor->parent; + switch (info->cursor->type) + { + case JSON_STRING: + if (info->cursor->parent == NULL) + return JSON_BAD_TREE_STRUCTURE; + else + { + info->cursor = info->cursor->parent; + if (info->cursor->type != JSON_OBJECT) + { + return JSON_BAD_TREE_STRUCTURE; + } + + info->state = 3; /* followup to adding child to array */ + } + break; + + case JSON_ARRAY: + info->state = 9; /* followup to adding child to array */ + break; + + default: + return JSON_BAD_TREE_STRUCTURE; + } + } + break; + + case LEX_BEGIN_OBJECT: + info->state = 1; /* open object */ + break; + + case LEX_MORE: + return JSON_INCOMPLETE_DOCUMENT; + break; + + case LEX_INVALID_CHARACTER: + return JSON_ILLEGAL_CHARACTER; + break; + + default: + printf ("state %d: defaulted\n", info->state); + return JSON_MALFORMED_DOCUMENT; + break; + } + } + break; + + case 9: /* followup to adding child to array */ + { + switch (lexer (buffer, &info->p, &info->lex_state, & info->lex_text)) + { + case LEX_VALUE_SEPARATOR: + info->state = 8; + break; + + case LEX_END_ARRAY: + if (info->cursor->parent == NULL) + { + info->state = 99; /* finished document. only accept whitespaces until EOF */ + } + else + { + info->cursor = info->cursor->parent; + switch (info->cursor->type) + { + case JSON_STRING: + if (info->cursor->parent == NULL) + { + info->state = 99; /* finished document. only accept whitespaces until EOF */ + } + else + { + info->cursor = info->cursor->parent; + if (info->cursor->type != JSON_OBJECT) + { + return JSON_BAD_TREE_STRUCTURE; + } + else + { + info->state = 3; /* followup to adding child to array */ + } + } + break; + + case JSON_ARRAY: + info->state = 9; /* followup to adding child to array */ + break; + + default: + return JSON_BAD_TREE_STRUCTURE; + } + } + break; + + case LEX_MORE: + return JSON_INCOMPLETE_DOCUMENT; + break; + + default: + printf ("state %d: defaulted\n", info->state); + return JSON_MALFORMED_DOCUMENT; + break; + } + } + break; + + case 99: /* finished document. only accept whitespaces until EOF */ + { + switch (lexer (buffer, &info->p, &info->lex_state, & info->lex_text)) + { + case LEX_MORE: + return JSON_WAITING_FOR_EOF; + break; + + case LEX_MEMORY: + return JSON_MEMORY; + break; + + default: + return JSON_MALFORMED_DOCUMENT; + break; + } + } + break; + + default: + printf ("invalid parser state %d: defaulted\n", info->state); + return JSON_UNKNOWN_PROBLEM; + } + } + info->p = NULL; + if (info->state == 99) + return JSON_WAITING_FOR_EOF; + else + return JSON_INCOMPLETE_DOCUMENT; +} +
Another abomination, again hopefully this won't have to be changed !
+enum json_error +json_saxy_parse (struct json_saxy_parser_status *jsps, struct json_saxy_functions *jsf, char c) +{ + /*TODO handle a string instead of a single char */ + /* temp variables */ + rcstring *temp; + + temp = NULL; + + /* goto where we left off */ + switch (jsps->state) + { + case 0: /* general state. everything goes. */ + goto state0; + break; + case 1: /* parse string */ + goto state1; + break; + case 2: /* parse string: escaped character */ + goto state2; + break; + case 3: /* parse string: escaped unicode 1 */ + goto state3; + break; + case 4: /* parse string: escaped unicode 2 */ + goto state4; + break; + case 5: /* parse string: escaped unicode 3 */ + goto state5; + break; + case 6: /* parse string: escaped unicode 4 */ + goto state6; + break; + case 7: /* parse true: tr */ + goto state7; + break; + case 8: /* parse true: tru */ + goto state8; + break; + case 9: /* parse true: true */ + goto state9; + break; + case 10: /* parse false: fa */ + goto state10; + break; + case 11: /* parse false: fal */ + goto state11; + break; + case 12: /* parse false: fals */ + goto state12; + break; + case 13: /* parse false: false */ + goto state13; + break; + case 14: /* parse null: nu */ + goto state14; + break; + case 15: /* parse null: nul */ + goto state15; + break; + case 16: /* parse null: null */ + goto state16; + break; + case 17: /* parse number: 0 */ + goto state17; + break; + case 18: /* parse number: start fraccional part */ + goto state18; + break; + case 19: /* parse number: fraccional part */ + goto state19; + break; + case 20: /* parse number: start exponent part */ + goto state20; + break; + case 21: /* parse number: exponent part */ + goto state21; + break; + case 22: /* parse number: exponent sign part */ + goto state22; + break; + case 23: /* parse number: start negative */ + goto state23; + break; + case 24: /* parse number: decimal part */ + goto state24; + break; + case 25: /* open object */ + goto state25; + break; + case 26: /* close object/array */ + goto state26; + break; + case 27: /* sibling followup */ + goto state27; + break; + + default: /* oops... this should never be reached */ + return JSON_UNKNOWN_PROBLEM; + } + +state0: /* starting point */ + { + switch (c) + { + case '\x20': + case '\x09': + case '\x0A': + case '\x0D': /* JSON insignificant white spaces */ + break; + + case '\"': /* starting a string */ + jsps->string_length_limit_reached = 0; + jsps->state = 1; + break; + + case '{': + if (jsf->open_object != NULL) + jsf->open_object (); + jsps->state = 25; /*open object */ + break; + + case '}': + if (jsf->close_object != NULL) + jsf->close_object (); + jsps->state = 26; /* close object/array */ + break; + + case '[': + if (jsf->open_array != NULL) + jsf->open_array (); +/* jsps->state = 0; // redundant*/ + break; + + case ']': + if (jsf->close_array != NULL) + jsf->close_array (); + jsps->state = 26; /* close object/array */ + break; + + case 't': + jsps->state = 7; /* parse true: tr */ + break; + + case 'f': + jsps->state = 10; /* parse false: fa */ + break; + + case 'n': + jsps->state = 14; /* parse null: nu */ + break; + + case ':': + if (jsf->label_value_separator != NULL) + jsf->label_value_separator (); +/* jsps->state = 0; // redundant*/ + break; + + case ',': + if (jsf->sibling_separator != NULL) + jsf->sibling_separator (); + jsps->state = 27; /* sibling followup */ + break; + + case '0': + jsps->string_length_limit_reached = 0; + jsps->state = 17; /* parse number: 0 */ + if ((jsps->temp = rcs_create (5)) == NULL) + { + return JSON_MEMORY; + } + if (rcs_catc ((jsps->temp), '0') != RS_OK) + { + return JSON_MEMORY; + } + break; + + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + jsps->string_length_limit_reached = 0; + jsps->state = 24; /* parse number: decimal */ + if ((jsps->temp = rcs_create (5)) == NULL) + { + return JSON_MEMORY; + } + if (rcs_catc ((jsps->temp), c) != RS_OK) + { + return JSON_MEMORY; + } + break; + + case '-': + jsps->string_length_limit_reached = 0; + jsps->state = 23; /* number: */ + jsps->temp = NULL; + if ((jsps->temp = rcs_create (5)) == NULL) + { + return JSON_MEMORY; + } + if (rcs_catc ((jsps->temp), '-') != RS_OK) + { + return JSON_MEMORY; + } + + break; + + default: + return JSON_ILLEGAL_CHARACTER; + break; + } + return JSON_OK; + } + +state1: /* parse string */ + { + switch (c) + { + case '\\': + if (!jsps->string_length_limit_reached) + { + if (rcs_length ((jsps->temp)) < JSON_MAX_STRING_LENGTH - 1) /* check if there is space for a two character escape sequence */ + { + if (rcs_catc ((jsps->temp), '\\') != RS_OK) + { + return JSON_MEMORY; + } + } + else + { + jsps->string_length_limit_reached = 1; + } + } + jsps->state = 2; /* parse string: escaped character */ + break; + + case '\"': /* end of string */ + if ((jsps->temp) != NULL) + { + jsps->state = 0; /* starting point */ + if (jsf->new_string != NULL) + jsf->new_string (((jsps->temp))->text); /*copied or integral? */ + rcs_free (& jsps->temp); + } + else + return JSON_UNKNOWN_PROBLEM; + break; + + default: + if (!jsps->string_length_limit_reached) + { + if (rcs_length ((jsps->temp)) < JSON_MAX_STRING_LENGTH) /* check if there is space for a two character escape sequence */ + { + if (rcs_catc ((jsps->temp), c) != RS_OK) + { + return JSON_MEMORY; + } + } + else + { + jsps->string_length_limit_reached = 1; + } + } + break; + } + return JSON_OK; + } + +state2: /* parse string: escaped character */ + { + switch (c) + { + case '\"': + case '\\': + case '/': + case 'b': + case 'f': + case 'n': + case 'r': + case 't': + if (!jsps->string_length_limit_reached) + { + if (rcs_length ((jsps->temp)) < JSON_MAX_STRING_LENGTH) + { + if (rcs_catc ((jsps->temp), c) != RS_OK) + { + return JSON_MEMORY; + } + } + else + { + jsps->string_length_limit_reached = 1; + } + } + break; + + case 'u': + if (!jsps->string_length_limit_reached) + { + if (rcs_length ((jsps->temp)) < JSON_MAX_STRING_LENGTH - 4) + { + if (rcs_catc ((jsps->temp), 'u') != RS_OK) + { + return JSON_MEMORY; + } + } + else + { + jsps->string_length_limit_reached = 1; + } + } + jsps->state = 3; /* parse string: escaped unicode 1; */ + break; + + default: + return JSON_ILLEGAL_CHARACTER; + break; + } + return JSON_OK; + } + +state3: /* parse string: escaped unicode 1 */ + { + switch (c) + { + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + case 'a': + case 'b': + case 'c': + case 'd': + case 'e': + case 'f': + case 'A': + case 'B': + case 'C': + case 'D': + case 'E': + case 'F': + if (!jsps->string_length_limit_reached) + { + if (rcs_length ((jsps->temp)) < JSON_MAX_STRING_LENGTH - 3) + { + if (rcs_catc ((jsps->temp), 'u') != RS_OK) + { + return JSON_MEMORY; + } + } + else + { + jsps->string_length_limit_reached = 1; + } + } + jsps->state = 4; /* parse string. escaped unicode 2 */ + break; + + default: + return JSON_ILLEGAL_CHARACTER; + } + return JSON_OK; + } + +state4: /* parse string: escaped unicode 2 */ + { + switch (c) + { + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + case 'a': + case 'b': + case 'c': + case 'd': + case 'e': + case 'f': + case 'A': + case 'B': + case 'C': + case 'D': + case 'E': + case 'F': + if (!jsps->string_length_limit_reached) + { + if (rcs_length ((jsps->temp)) < JSON_MAX_STRING_LENGTH - 2) + { + if (rcs_catc ((jsps->temp), c) != RS_OK) + { + return JSON_MEMORY; + } + } + else + { + jsps->string_length_limit_reached = 1; + } + } + jsps->state = 5; /* parse string. escaped unicode 3 */ + break; + + default: + return JSON_ILLEGAL_CHARACTER; + } + return JSON_OK; + } + +state5: /* parse string: escaped unicode 3 */ + { + switch (c) + { + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + case 'a': + case 'b': + case 'c': + case 'd': + case 'e': + case 'f': + case 'A': + case 'B': + case 'C': + case 'D': + case 'E': + case 'F': + if (!jsps->string_length_limit_reached) + { + if (rcs_length ((jsps->temp)) < JSON_MAX_STRING_LENGTH - 1) + { + if (rcs_catc ((jsps->temp), c) != RS_OK) + { + return JSON_MEMORY; + } + } + else + { + jsps->string_length_limit_reached = 1; + } + } + jsps->state = 6; /* parse string. escaped unicode 4 */ + break; + + default: + return JSON_ILLEGAL_CHARACTER; + } + return JSON_OK; + } + +state6: /* parse string: escaped unicode 4 */ + { + switch (c) + { + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + case 'a': + case 'b': + case 'c': + case 'd': + case 'e': + case 'f': + case 'A': + case 'B': + case 'C': + case 'D': + case 'E': + case 'F': + if (!jsps->string_length_limit_reached) + { + if (rcs_length ((jsps->temp)) < JSON_MAX_STRING_LENGTH) + { + if (rcs_catc ((jsps->temp), c) != RS_OK) + { + return JSON_MEMORY; + } + } + else + { + jsps->string_length_limit_reached = 1; + } + } + jsps->state = 1; /* parse string */ + break; + + default: + return JSON_ILLEGAL_CHARACTER; + } + return JSON_OK; + } + +state7: /* parse true: tr */ + { + if (c != 'r') + { + return JSON_ILLEGAL_CHARACTER; + } + + jsps->state = 8; /* parse true: tru */ + return JSON_OK; + } + +state8: /* parse true: tru */ + { + if (c != 'u') + { + return JSON_ILLEGAL_CHARACTER; + } + + jsps->state = 9; /* parse true: true */ + return JSON_OK; + } + +state9: /* parse true: true */ + { + if (c != 'e') + { + return JSON_ILLEGAL_CHARACTER; + } + + jsps->state = 0; /* back to general state. */ + if (jsf->new_true != NULL) + jsf->new_true (); + return JSON_OK; + } + +state10: /* parse false: fa */ + { + if (c != 'a') + { + return JSON_ILLEGAL_CHARACTER; + } + + jsps->state = 11; /* parse true: fal */ + return JSON_OK; + } + +state11: /* parse false: fal */ + { + if (c != 'l') + { + return JSON_ILLEGAL_CHARACTER; + } + + jsps->state = 12; /* parse true: fals */ + return JSON_OK; + } + +state12: /* parse false: fals */ + { + if (c != 's') + { + return JSON_ILLEGAL_CHARACTER; + } + + jsps->state = 13; /* parse true: false */ + return JSON_OK; + } + +state13: /* parse false: false */ + { + if (c != 'e') + { + return JSON_ILLEGAL_CHARACTER; + } + + jsps->state = 0; /* general state. everything goes. */ + if (jsf->new_false != NULL) + jsf->new_false (); + return JSON_OK; + } + +state14: /* parse null: nu */ + { + if (c != 'u') + { + return JSON_ILLEGAL_CHARACTER; + } + + jsps->state = 15; /* parse null: nul */ + return JSON_OK; + } + +state15: /* parse null: nul */ + { + if (c != 'l') + { + return JSON_ILLEGAL_CHARACTER; + } + + jsps->state = 16; /* parse null: null */ + return JSON_OK; + } + +state16: /* parse null: null */ + { + if (c != 'l') + { + return JSON_ILLEGAL_CHARACTER; + } + + jsps->state = 0; /* general state. everything goes. */ + if (jsf->new_null != NULL) + jsf->new_null (); + return JSON_OK; + } + +state17: /* parse number: 0 */ + { + switch (c) + { + case '.': + if ((jsps->temp = rcs_create (5)) == NULL) + { + return JSON_MEMORY; + } + if (rcs_catc ((jsps->temp), '.') != RS_OK) + { + return JSON_MEMORY; + } + jsps->state = 18; /* parse number: fraccional part */ + break; + + case '\x20': + case '\x09': + case '\x0A': + case '\x0D': /* JSON insignificant white spaces */ + if ((jsps->temp) == NULL) + return JSON_MEMORY; + if (jsf->new_number != NULL) + { + jsf->new_number ((jsps->temp)->text); + } + rcs_free (& jsps->temp); + + jsps->state = 0; + break; + + case '}': + if ((jsps->temp) == NULL) + return JSON_MEMORY; + if (jsf->new_number != NULL) + { + jsf->new_number ((jsps->temp)->text); + } + rcs_free (& jsps->temp); + + if (jsf->open_object != NULL) + jsf->close_object (); + jsps->state = 26; /* close object/array */ + break; + + case ']': + + if ((jsps->temp) == NULL) + return JSON_MEMORY; + if (jsf->new_number != NULL) + { + jsf->new_number ((jsps->temp)->text); + } + rcs_free (& jsps->temp); + + if (jsf->open_object != NULL) + jsf->close_array (); + jsps->state = 26; /* close object/array */ + break; + + case ',': + + if ((jsps->temp) == NULL) + return JSON_MEMORY; + if (jsf->new_number != NULL) + { + jsf->new_number ((jsps->temp)->text); + } + rcs_free (& jsps->temp); + + if (jsf->open_object != NULL) + jsf->label_value_separator (); + jsps->state = 27; /* sibling followup */ + break; + + default: + return JSON_ILLEGAL_CHARACTER; + break; + } + + return JSON_OK; + } + +state18: /* parse number: start fraccional part */ + { + switch (c) + { + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + if (!jsps->string_length_limit_reached) + { + if (rcs_length ((jsps->temp)) < JSON_MAX_STRING_LENGTH / 2) + { + if (rcs_catc ((jsps->temp), c) != RS_OK) + { + return JSON_MEMORY; + } + } + else + { + jsps->string_length_limit_reached = 1; + } + } + jsps->state = 19; /* parse number: fractional part */ + break; + + default: + return JSON_ILLEGAL_CHARACTER; + break; + } + return JSON_OK; + } + +state19: /* parse number: fraccional part */ + { + switch (c) + { + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + if (!jsps->string_length_limit_reached) + { + if (rcs_length ((jsps->temp)) < JSON_MAX_STRING_LENGTH / 2) + { + if (rcs_catc ((jsps->temp), c) != RS_OK) + { + return JSON_MEMORY; + } + } + else + { + jsps->string_length_limit_reached = 1; + } + } +/* jsps->state = 19; // parse number: fractional part*/ + break; + + case 'e': + case 'E': + if (rcs_catc ((jsps->temp), c) != RS_OK) + { + return JSON_MEMORY; + } + + jsps->state = 20; /* parse number: start exponent part */ + break; + + + case '\x20': + case '\x09': + case '\x0A': + case '\x0D': /* JSON insignificant white spaces */ + + if ((jsps->temp) == NULL) + return JSON_MEMORY; + if (jsf->new_number != NULL) + { + jsf->new_number ((jsps->temp)->text); + } + rcs_free (& jsps->temp); + + jsps->state = 0; + break; + + case '}': + + if ((jsps->temp) == NULL) + return JSON_MEMORY; + if (jsf->new_number != NULL) + { + jsf->new_number ((jsps->temp)->text); + } + rcs_free (& jsps->temp); + + if (jsf->open_object != NULL) + jsf->close_object (); + jsps->state = 26; /* close object/array */ + break; + + case ']': + if (jsf->new_number != NULL) + { + if ((jsps->temp) == NULL) + return JSON_MEMORY; + jsf->new_number ((jsps->temp)->text); + rcs_free (& jsps->temp); + } + else + { + rcs_free (& jsps->temp); + jsps->temp = NULL; + } + if (jsf->open_object != NULL) + jsf->close_array (); + jsps->state = 26; /* close object/array */ + break; + + case ',': + + if ((jsps->temp) == NULL) + return JSON_MEMORY; + if (jsf->new_number != NULL) + { + jsf->new_number ((jsps->temp)->text); + } + rcs_free (& jsps->temp); + + if (jsf->label_value_separator != NULL) + jsf->label_value_separator (); + jsps->state = 27; /* sibling followup */ + break; + + + default: + return JSON_ILLEGAL_CHARACTER; + break; + } + return JSON_OK; + } + +state20: /* parse number: start exponent part */ + { + switch (c) + { + case '+': + case '-': + jsps->string_length_limit_reached = 0; + if (rcs_catc ((jsps->temp), c) != RS_OK) + { + return JSON_MEMORY; + } + + jsps->state = 22; /* parse number: exponent sign part */ + break; + + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + if (!jsps->string_length_limit_reached) + { + if (rcs_length ((jsps->temp)) < JSON_MAX_STRING_LENGTH) + { + if (rcs_catc ((jsps->temp), c) != RS_OK) + { + return JSON_MEMORY; + } + + } + else + { + jsps->string_length_limit_reached = 1; + } + } + jsps->state = 21; /* parse number: exponent part */ + break; + + default: + return JSON_ILLEGAL_CHARACTER; + break; + } + return JSON_OK; + } + +state21: /* parse number: exponent part */ + { + switch (c) + { + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + if (!jsps->string_length_limit_reached) + { + if (rcs_length ((jsps->temp)) < JSON_MAX_STRING_LENGTH) + { + if (rcs_catc ((jsps->temp), c) != RS_OK) + { + return JSON_MEMORY; + } + } + else + { + jsps->string_length_limit_reached = 1; + } + } +/* jsps->state = 21; // parse number: exponent part*/ + break; + + case '\x20': + case '\x09': + case '\x0A': + case '\x0D': /* JSON insignificant white spaces */ + + if ((jsps->temp) == NULL) + return JSON_MEMORY; + if (jsf->new_number != NULL) + { + jsf->new_number ((jsps->temp)->text); + } + rcs_free (& jsps->temp); + + jsps->state = 0; + break; + + case '}': + if ((jsps->temp) == NULL) + return JSON_MEMORY; + if (jsf->new_number != NULL) + { + jsf->new_number ((jsps->temp)->text); + } + rcs_free (& jsps->temp); + + if (jsf->open_object != NULL) + jsf->close_object (); + jsps->state = 26; /* close object */ + break; + + case ']': + if (jsf->new_number != NULL) + { + if ((jsps->temp) == NULL) + return JSON_MEMORY; + jsf->new_number ((jsps->temp)->text); + VIR_FREE(jsps->temp); + } + else + { + VIR_FREE(jsps->temp); + jsps->temp = NULL; + } + if (jsf->open_object != NULL) + jsf->close_array (); + jsps->state = 26; /* close object/array */ + break; + + case ',': + if (jsf->new_number != NULL) + { + if ((jsps->temp) == NULL) + return JSON_MEMORY; + jsf->new_number ((jsps->temp)->text); + VIR_FREE(jsps->temp); + } + else + { + free (jsps->temp); + jsps->temp = NULL; + } + if (jsf->label_value_separator != NULL) + jsf->label_value_separator (); + jsps->state = 27; /* sibling followup */ + break; + + default: + return JSON_ILLEGAL_CHARACTER; + break; + } + return JSON_OK; + } + +state22: /* parse number: start exponent part */ + { + switch (c) + { + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + if (!jsps->string_length_limit_reached) + { + if (rcs_length ((jsps->temp)) < JSON_MAX_STRING_LENGTH) + { + rcs_catc ((jsps->temp), c); + } + else + { + jsps->string_length_limit_reached = 1; + } + } + jsps->state = 21; /* parse number: exponent part */ + break; + + default: + return JSON_ILLEGAL_CHARACTER; + break; + } + return JSON_OK; + } + +state23: /* parse number: start negative */ + { + switch (c) + { + case '0': + rcs_catc ((jsps->temp), c); + jsps->state = 17; /* parse number: 0 */ + break; + + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + if (!jsps->string_length_limit_reached) + { + if (rcs_length ((jsps->temp)) < JSON_MAX_STRING_LENGTH / 2) + { + if ((jsps->temp = rcs_create (5)) == NULL) + { + return JSON_MEMORY; + } + if (rcs_catc ((jsps->temp), c) != RS_OK) + { + return JSON_MEMORY; + } + else + { + jsps->string_length_limit_reached = 1; + } + } + } + jsps->state = 24; /* parse number: start decimal part */ + break; + + default: + return JSON_ILLEGAL_CHARACTER; + break; + } + return JSON_OK; + } + +state24: /* parse number: decimal part */ + { + switch (c) + { + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + if (!jsps->string_length_limit_reached) + { + if (rcs_length ((jsps->temp)) < JSON_MAX_STRING_LENGTH / 2) + { + if ((jsps->temp = rcs_create (5)) == NULL) + { + return JSON_MEMORY; + } + if (rcs_catc ((jsps->temp), c) != RS_OK) + { + return JSON_MEMORY; + } + } + else + { + jsps->string_length_limit_reached = 1; + } + } +/* jsps->state = 24; // parse number: decimal part*/ + break; + + case '.': + if ((jsps->temp = rcs_create (5)) == NULL) + { + return JSON_MEMORY; + } + if (rcs_catc ((jsps->temp), '.') != RS_OK) + { + return JSON_MEMORY; + } + + jsps->state = 18; /* parse number: start exponent part */ + break; + + case 'e': + case 'E': + if ((jsps->temp = rcs_create (5)) == NULL) + { + return JSON_MEMORY; + } + if (rcs_catc ((jsps->temp), c) != RS_OK) + { + return JSON_MEMORY; + } + + jsps->string_length_limit_reached = 0; /* reset to accept the exponential part */ + jsps->state = 20; /* parse number: start exponent part */ + break; + + case '\x20': + case '\x09': + case '\x0A': + case '\x0D': /* JSON insignificant white spaces */ + if ((jsps->temp) == NULL) + return JSON_MEMORY; + if (jsf->new_number != NULL) + { + jsf->new_number ((jsps->temp)->text); + } + rcs_free (& jsps->temp); + + jsps->state = 0; + break; + + case '}': + if ((jsps->temp) == NULL) + return JSON_MEMORY; + if (jsf->new_number != NULL) + { + jsf->new_number ((jsps->temp)->text); + } + rcs_free (& jsps->temp); + + if (jsf->open_object != NULL) + jsf->close_object (); + jsps->state = 26; /* close object/array */ + break; + + case ']': + if ((jsps->temp) == NULL) + return JSON_MEMORY; + if (jsf->new_number != NULL) + { + jsf->new_number ((jsps->temp)->text); + } + rcs_free (& jsps->temp); + + if (jsf->open_object != NULL) + jsf->close_array (); + jsps->state = 26; /* close object/array */ + break; + + case ',': + if ((jsps->temp) == NULL) + return JSON_MEMORY; + if (jsf->new_number != NULL) + { + jsf->new_number ((jsps->temp)->text); + } + rcs_free (& jsps->temp); + + if (jsf->label_value_separator != NULL) + jsf->label_value_separator (); + jsps->state = 27; /* sibling followup */ + break; + + default: + return JSON_ILLEGAL_CHARACTER; + break; + } + return JSON_OK; + } + +state25: /* open object */ + { + switch (c) + { + case '\x20': + case '\x09': + case '\x0A': + case '\x0D': /* JSON insignificant white spaces */ + break; + + case '\"': + jsps->temp = NULL; + jsps->state = 1; + break; + + case '}': + if (jsf->close_object != NULL) + jsf->close_object (); + jsps->state = 26; /* close object */ + break; + + default: + return JSON_ILLEGAL_CHARACTER; + break; + } + return JSON_OK; + } + +state26: /* close object/array */ + { + switch (c) + { + case '\x20': + case '\x09': + case '\x0A': + case '\x0D': /* JSON insignificant white spaces */ + break; + + case '}': + if (jsf->close_object != NULL) + jsf->close_object (); +/* jsp->state = 26; // close object*/ + break; + + case ']': + if (jsf->close_array != NULL) + jsf->close_array (); +/* jsps->state = 26; // close object/array*/ + break; + + case ',': + if (jsf->sibling_separator != NULL) + jsf->sibling_separator (); + jsps->state = 27; /* sibling followup */ + break; + + default: + return JSON_ILLEGAL_CHARACTER; + break; + } + return JSON_OK; + } + +state27: /* sibling followup */ + { + switch (c) + { + case '\x20': + case '\x09': + case '\x0A': + case '\x0D': /* JSON insignificant white spaces */ + break; + + case '\"': + jsps->state = 1; + jsps->temp = NULL; + break; + + case '{': + if (jsf->open_object != NULL) + jsf->open_object (); + jsps->state = 25; /*open object */ + break; + + case '[': + if (jsf->open_array != NULL) + jsf->open_array (); +/* jsps->state = 0; // redundant*/ + break; + + case 't': + jsps->state = 7; /* parse true: tr */ + break; + + case 'f': + jsps->state = 10; /* parse false: fa */ + break; + + case 'n': + jsps->state = 14; /* parse null: nu */ + break; + + case '0': + jsps->state = 17; /* parse number: 0 */ + if ((jsps->temp = rcs_create (5)) == NULL) + { + return JSON_MEMORY; + } + if (rcs_catc ((jsps->temp), '0') != RS_OK) + { + return JSON_MEMORY; + } + break; + + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + jsps->state = 24; /* parse number: decimal */ + if ((jsps->temp = rcs_create (5)) == NULL) + { + return JSON_MEMORY; + } + if (rcs_catc ((jsps->temp), c) != RS_OK) + { + return JSON_MEMORY; + } + break; + + case '-': + jsps->state = 23; /* number: */ + if ((jsps->temp = rcs_create (RSTRING_DEFAULT)) == NULL) + { + return JSON_MEMORY; + } + if (rcs_catc ((jsps->temp), '-') != RS_OK) + { + return JSON_MEMORY; + } + break; + + default: + return JSON_ILLEGAL_CHARACTER; + break; + } + return JSON_OK; + } + + return JSON_UNKNOWN_PROBLEM; +}
Urgh ... Okay his parsing style and mine don't match ! but come on lex/yacc was invented to avoid having to maintain such code. I'm not objecting, as long as we don't have to fix this code, that's fine. ACK Daniel -- Daniel Veillard | libxml Gnome XML XSLT toolkit http://xmlsoft.org/ daniel@veillard.com | Rpmfind RPM search engine http://rpmfind.net/ http://veillard.com/ | virtualization library http://libvirt.org/

Initial support for the new QEMU monitor protocol using JSON as the data encoding format instead of plain text * src/qemu/qemu_conf.c, src/qemu/qemu_conf.h: Hack to turn on QMP mode * src/qemu/qemu_monitor.c: Delegate to json monitor if enabled * src/qemu/qemu_monitor_json.c, src/qemu/qemu_monitor_json.h: Add impl of QMP protocol * src/Makefile.am: Add src/qemu/qemu_monitor_json.{c,h} --- po/POTFILES.in | 1 + src/Makefile.am | 2 + src/qemu/qemu_conf.c | 10 +- src/qemu/qemu_conf.h | 3 + src/qemu/qemu_monitor.c | 260 +++++++++++--- src/qemu/qemu_monitor_json.c | 817 ++++++++++++++++++++++++++++++++++++++++++ src/qemu/qemu_monitor_json.h | 155 ++++++++ 7 files changed, 1201 insertions(+), 47 deletions(-) create mode 100644 src/qemu/qemu_monitor_json.c create mode 100644 src/qemu/qemu_monitor_json.h diff --git a/po/POTFILES.in b/po/POTFILES.in index 5b9a364..2fcb9bf 100644 --- a/po/POTFILES.in +++ b/po/POTFILES.in @@ -27,6 +27,7 @@ src/phyp/phyp_driver.c src/qemu/qemu_conf.c src/qemu/qemu_driver.c src/qemu/qemu_monitor.c +src/qemu/qemu_monitor_json.c src/qemu/qemu_monitor_text.c src/remote/remote_driver.c src/secret/secret_driver.c diff --git a/src/Makefile.am b/src/Makefile.am index 9660a99..dce5792 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -186,6 +186,8 @@ QEMU_DRIVER_SOURCES = \ qemu/qemu_monitor.c qemu/qemu_monitor.h \ qemu/qemu_monitor_text.c \ qemu/qemu_monitor_text.h \ + qemu/qemu_monitor_json.c \ + qemu/qemu_monitor_json.h \ qemu/qemu_driver.c qemu/qemu_driver.h UML_DRIVER_SOURCES = \ diff --git a/src/qemu/qemu_conf.c b/src/qemu/qemu_conf.c index b01fddc..d03d522 100644 --- a/src/qemu/qemu_conf.c +++ b/src/qemu/qemu_conf.c @@ -1748,10 +1748,18 @@ int qemudBuildCommandLine(virConnectPtr conn, ADD_ARG_LIT("-nographic"); if (monitor_chr) { + /* XXX gross hack for testing */ +#if QEMU_WITH_JSON + char buf[4096] = "control,"; + + if (qemudBuildCommandLineChrDevStr(monitor_chr, buf + 8, sizeof(buf)-8) < 0) + goto error; +#else char buf[4096]; - if (qemudBuildCommandLineChrDevStr(monitor_chr, buf, sizeof(buf)) < 0) + if (qemudBuildCommandLineChrDevStr(monitor_chr, buf, sizeof(buf)-8) < 0) goto error; +#endif ADD_ARG_LIT("-monitor"); ADD_ARG_LIT(buf); diff --git a/src/qemu/qemu_conf.h b/src/qemu/qemu_conf.h index f9a970f..336e30d 100644 --- a/src/qemu/qemu_conf.h +++ b/src/qemu/qemu_conf.h @@ -39,6 +39,9 @@ #define qemudDebug(fmt, ...) do {} while(0) +/* XXX gross hack for testing purposes */ +#define QEMU_WITH_JSON 0 + #define QEMUD_CPUMASK_LEN CPU_SETSIZE /* Internal flags to keep track of qemu command line capabilities */ diff --git a/src/qemu/qemu_monitor.c b/src/qemu/qemu_monitor.c index 501cb3f..573bee9 100644 --- a/src/qemu/qemu_monitor.c +++ b/src/qemu/qemu_monitor.c @@ -30,6 +30,7 @@ #include "qemu_monitor.h" #include "qemu_monitor_text.h" +#include "qemu_monitor_json.h" #include "qemu_conf.h" #include "event.h" #include "virterror_internal.h" @@ -71,6 +72,8 @@ struct _qemuMonitor { unsigned eofcb: 1; /* If the monitor callback should free the closed monitor */ unsigned closed: 1; + + unsigned json: 1; }; void qemuMonitorLock(qemuMonitorPtr mon) @@ -180,9 +183,14 @@ qemuMonitorIOProcess(qemuMonitorPtr mon) msg = mon->msg; VIR_DEBUG("Process %d", mon->bufferOffset); - len = qemuMonitorTextIOProcess(mon, - mon->buffer, mon->bufferOffset, - msg); + if (mon->json) + len = qemuMonitorJSONIOProcess(mon, + mon->buffer, mon->bufferOffset, + msg); + else + len = qemuMonitorTextIOProcess(mon, + mon->buffer, mon->bufferOffset, + msg); if (len < 0) { mon->lastErrno = errno; @@ -455,6 +463,7 @@ qemuMonitorOpen(virDomainObjPtr vm, mon->fd = -1; mon->vm = vm; mon->eofCB = eofCB; + mon->json = QEMU_WITH_JSON; qemuMonitorLock(mon); switch (vm->monitor_chr->type) { @@ -595,43 +604,68 @@ int qemuMonitorStartCPUs(qemuMonitorPtr mon, virConnectPtr conn) { + int ret; DEBUG("mon=%p, fd=%d", mon, mon->fd); - return qemuMonitorTextStartCPUs(mon, conn); + if (mon->json) + ret = qemuMonitorJSONStartCPUs(mon, conn); + else + ret = qemuMonitorTextStartCPUs(mon, conn); + return ret; } int qemuMonitorStopCPUs(qemuMonitorPtr mon) { + int ret; DEBUG("mon=%p, fd=%d", mon, mon->fd); - return qemuMonitorTextStopCPUs(mon); + if (mon->json) + ret = qemuMonitorJSONStopCPUs(mon); + else + ret = qemuMonitorTextStopCPUs(mon); + return ret; } int qemuMonitorSystemPowerdown(qemuMonitorPtr mon) { + int ret; DEBUG("mon=%p, fd=%d", mon, mon->fd); - return qemuMonitorTextSystemPowerdown(mon); + if (mon->json) + ret = qemuMonitorJSONSystemPowerdown(mon); + else + ret = qemuMonitorTextSystemPowerdown(mon); + return ret; } int qemuMonitorGetCPUInfo(qemuMonitorPtr mon, int **pids) { + int ret; DEBUG("mon=%p, fd=%d", mon, mon->fd); - return qemuMonitorTextGetCPUInfo(mon, pids); + if (mon->json) + ret = qemuMonitorJSONGetCPUInfo(mon, pids); + else + ret = qemuMonitorTextGetCPUInfo(mon, pids); + return ret; } int qemuMonitorGetBalloonInfo(qemuMonitorPtr mon, unsigned long *currmem) { + int ret; DEBUG("mon=%p, fd=%d", mon, mon->fd); - return qemuMonitorTextGetBalloonInfo(mon, currmem); + if (mon->json) + ret = qemuMonitorJSONGetBalloonInfo(mon, currmem); + else + ret = qemuMonitorTextGetBalloonInfo(mon, currmem); + return ret; } @@ -643,38 +677,61 @@ int qemuMonitorGetBlockStatsInfo(qemuMonitorPtr mon, long long *wr_bytes, long long *errs) { + int ret; DEBUG("mon=%p, fd=%d dev=%s", mon, mon->fd, devname); - return qemuMonitorTextGetBlockStatsInfo(mon, devname, - rd_req, rd_bytes, - wr_req, wr_bytes, - errs); + if (mon->json) + ret = qemuMonitorJSONGetBlockStatsInfo(mon, devname, + rd_req, rd_bytes, + wr_req, wr_bytes, + errs); + else + ret = qemuMonitorTextGetBlockStatsInfo(mon, devname, + rd_req, rd_bytes, + wr_req, wr_bytes, + errs); + return ret; } int qemuMonitorSetVNCPassword(qemuMonitorPtr mon, const char *password) { + int ret; DEBUG("mon=%p, fd=%d", mon, mon->fd); - return qemuMonitorTextSetVNCPassword(mon, password); + if (mon->json) + ret = qemuMonitorJSONSetVNCPassword(mon, password); + else + ret = qemuMonitorTextSetVNCPassword(mon, password); + return ret; } int qemuMonitorSetBalloon(qemuMonitorPtr mon, unsigned long newmem) { + int ret; DEBUG("mon=%p, fd=%d newmem=%lu", mon, mon->fd, newmem); - return qemuMonitorTextSetBalloon(mon, newmem); + if (mon->json) + ret = qemuMonitorJSONSetBalloon(mon, newmem); + else + ret = qemuMonitorTextSetBalloon(mon, newmem); + return ret; } int qemuMonitorEjectMedia(qemuMonitorPtr mon, const char *devname) { + int ret; DEBUG("mon=%p, fd=%d devname=%s", mon, mon->fd, devname); - return qemuMonitorTextEjectMedia(mon, devname); + if (mon->json) + ret = qemuMonitorJSONEjectMedia(mon, devname); + else + ret = qemuMonitorTextEjectMedia(mon, devname); + return ret; } @@ -682,10 +739,15 @@ int qemuMonitorChangeMedia(qemuMonitorPtr mon, const char *devname, const char *newmedia) { + int ret; DEBUG("mon=%p, fd=%d devname=%s newmedia=%s", mon, mon->fd, devname, newmedia); - return qemuMonitorTextChangeMedia(mon, devname, newmedia); + if (mon->json) + ret = qemuMonitorJSONChangeMedia(mon, devname, newmedia); + else + ret = qemuMonitorTextChangeMedia(mon, devname, newmedia); + return ret; } @@ -694,10 +756,15 @@ int qemuMonitorSaveVirtualMemory(qemuMonitorPtr mon, size_t length, const char *path) { + int ret; DEBUG("mon=%p, fd=%d offset=%llu length=%zu path=%s", mon, mon->fd, offset, length, path); - return qemuMonitorTextSaveVirtualMemory(mon, offset, length, path); + if (mon->json) + ret = qemuMonitorJSONSaveVirtualMemory(mon, offset, length, path); + else + ret = qemuMonitorTextSaveVirtualMemory(mon, offset, length, path); + return ret; } int qemuMonitorSavePhysicalMemory(qemuMonitorPtr mon, @@ -705,19 +772,29 @@ int qemuMonitorSavePhysicalMemory(qemuMonitorPtr mon, size_t length, const char *path) { + int ret; DEBUG("mon=%p, fd=%d offset=%llu length=%zu path=%s", mon, mon->fd, offset, length, path); - return qemuMonitorTextSavePhysicalMemory(mon, offset, length, path); + if (mon->json) + ret = qemuMonitorJSONSavePhysicalMemory(mon, offset, length, path); + else + ret = qemuMonitorTextSavePhysicalMemory(mon, offset, length, path); + return ret; } int qemuMonitorSetMigrationSpeed(qemuMonitorPtr mon, unsigned long bandwidth) { + int ret; DEBUG("mon=%p, fd=%d bandwidth=%lu", mon, mon->fd, bandwidth); - return qemuMonitorTextSetMigrationSpeed(mon, bandwidth); + if (mon->json) + ret = qemuMonitorJSONSetMigrationSpeed(mon, bandwidth); + else + ret = qemuMonitorTextSetMigrationSpeed(mon, bandwidth); + return ret; } int qemuMonitorGetMigrationStatus(qemuMonitorPtr mon, @@ -726,12 +803,20 @@ int qemuMonitorGetMigrationStatus(qemuMonitorPtr mon, unsigned long long *remaining, unsigned long long *total) { + int ret; DEBUG("mon=%p, fd=%d", mon, mon->fd); - return qemuMonitorTextGetMigrationStatus(mon, status, - transferred, - remaining, - total); + if (mon->json) + ret = qemuMonitorJSONGetMigrationStatus(mon, status, + transferred, + remaining, + total); + else + ret = qemuMonitorTextGetMigrationStatus(mon, status, + transferred, + remaining, + total); + return ret; } @@ -740,10 +825,15 @@ int qemuMonitorMigrateToHost(qemuMonitorPtr mon, const char *hostname, int port) { + int ret; DEBUG("mon=%p, fd=%d hostname=%s port=%d", mon, mon->fd, hostname, port); - return qemuMonitorTextMigrateToHost(mon, background, hostname, port); + if (mon->json) + ret = qemuMonitorJSONMigrateToHost(mon, background, hostname, port); + else + ret = qemuMonitorTextMigrateToHost(mon, background, hostname, port); + return ret; } @@ -752,35 +842,55 @@ int qemuMonitorMigrateToCommand(qemuMonitorPtr mon, const char * const *argv, const char *target) { + int ret; DEBUG("mon=%p, fd=%d argv=%p target=%s", mon, mon->fd, argv, target); - return qemuMonitorTextMigrateToCommand(mon, background, argv, target); + if (mon->json) + ret = qemuMonitorJSONMigrateToCommand(mon, background, argv, target); + else + ret = qemuMonitorTextMigrateToCommand(mon, background, argv, target); + return ret; } int qemuMonitorMigrateToUnix(qemuMonitorPtr mon, int background, const char *unixfile) { + int ret; DEBUG("mon=%p fd=%d unixfile=%s", mon, mon->fd, unixfile); - return qemuMonitorTextMigrateToUnix(mon, background, unixfile); + if (mon->json) + ret = qemuMonitorJSONMigrateToUnix(mon, background, unixfile); + else + ret = qemuMonitorTextMigrateToUnix(mon, background, unixfile); + return ret; } int qemuMonitorMigrateCancel(qemuMonitorPtr mon) { + int ret; DEBUG("mon=%p fd=%d", mon, mon->fd); - return qemuMonitorTextMigrateCancel(mon); + if (mon->json) + ret = qemuMonitorJSONMigrateCancel(mon); + else + ret = qemuMonitorTextMigrateCancel(mon); + return ret; } int qemuMonitorAddUSBDisk(qemuMonitorPtr mon, const char *path) { + int ret; DEBUG("mon=%p, fd=%d path=%s", mon, mon->fd, path); - return qemuMonitorTextAddUSBDisk(mon, path); + if (mon->json) + ret = qemuMonitorJSONAddUSBDisk(mon, path); + else + ret = qemuMonitorTextAddUSBDisk(mon, path); + return ret; } @@ -788,19 +898,29 @@ int qemuMonitorAddUSBDeviceExact(qemuMonitorPtr mon, int bus, int dev) { + int ret; DEBUG("mon=%p, fd=%d bus=%d dev=%d", mon, mon->fd, bus, dev); - return qemuMonitorTextAddUSBDeviceExact(mon, bus, dev); + if (mon->json) + ret = qemuMonitorJSONAddUSBDeviceExact(mon, bus, dev); + else + ret = qemuMonitorTextAddUSBDeviceExact(mon, bus, dev); + return ret; } int qemuMonitorAddUSBDeviceMatch(qemuMonitorPtr mon, int vendor, int product) { + int ret; DEBUG("mon=%p, fd=%d vendor=%d product=%d", mon, mon->fd, vendor, product); - return qemuMonitorTextAddUSBDeviceMatch(mon, vendor, product); + if (mon->json) + ret = qemuMonitorJSONAddUSBDeviceMatch(mon, vendor, product); + else + ret = qemuMonitorTextAddUSBDeviceMatch(mon, vendor, product); + return ret; } @@ -813,16 +933,26 @@ int qemuMonitorAddPCIHostDevice(qemuMonitorPtr mon, unsigned *guestBus, unsigned *guestSlot) { + int ret; DEBUG("mon=%p, fd=%d domain=%d bus=%d slot=%d function=%d", mon, mon->fd, hostDomain, hostBus, hostSlot, hostFunction); - return qemuMonitorTextAddPCIHostDevice(mon, hostDomain, - hostBus, hostSlot, - hostFunction, - guestDomain, - guestBus, - guestSlot); + if (mon->json) + ret = qemuMonitorJSONAddPCIHostDevice(mon, hostDomain, + hostBus, hostSlot, + hostFunction, + guestDomain, + guestBus, + guestSlot); + else + ret = qemuMonitorTextAddPCIHostDevice(mon, hostDomain, + hostBus, hostSlot, + hostFunction, + guestDomain, + guestBus, + guestSlot); + return ret; } @@ -833,11 +963,17 @@ int qemuMonitorAddPCIDisk(qemuMonitorPtr mon, unsigned *guestBus, unsigned *guestSlot) { + int ret; DEBUG("mon=%p, fd=%d path=%s bus=%s", mon, mon->fd, path, bus); - return qemuMonitorTextAddPCIDisk(mon, path, bus, - guestDomain, guestBus, guestSlot); + if (mon->json) + ret = qemuMonitorJSONAddPCIDisk(mon, path, bus, + guestDomain, guestBus, guestSlot); + else + ret = qemuMonitorTextAddPCIDisk(mon, path, bus, + guestDomain, guestBus, guestSlot); + return ret; } @@ -847,10 +983,16 @@ int qemuMonitorAddPCINetwork(qemuMonitorPtr mon, unsigned *guestBus, unsigned *guestSlot) { + int ret; DEBUG("mon=%p, fd=%d nicstr=%s", mon, mon->fd, nicstr); - return qemuMonitorTextAddPCINetwork(mon, nicstr, guestDomain, - guestBus, guestSlot); + if (mon->json) + ret = qemuMonitorJSONAddPCINetwork(mon, nicstr, guestDomain, + guestBus, guestSlot); + else + ret = qemuMonitorTextAddPCINetwork(mon, nicstr, guestDomain, + guestBus, guestSlot); + return ret; } @@ -859,11 +1001,17 @@ int qemuMonitorRemovePCIDevice(qemuMonitorPtr mon, unsigned guestBus, unsigned guestSlot) { + int ret; DEBUG("mon=%p, fd=%d domain=%d bus=%d slot=%d", mon, mon->fd, guestDomain, guestBus, guestSlot); - return qemuMonitorTextRemovePCIDevice(mon, guestDomain, - guestBus, guestSlot); + if (mon->json) + ret = qemuMonitorJSONRemovePCIDevice(mon, guestDomain, + guestBus, guestSlot); + else + ret = qemuMonitorTextRemovePCIDevice(mon, guestDomain, + guestBus, guestSlot); + return ret; } @@ -871,30 +1019,45 @@ int qemuMonitorSendFileHandle(qemuMonitorPtr mon, const char *fdname, int fd) { + int ret; DEBUG("mon=%p, fd=%d fdname=%s fd=%d", mon, mon->fd, fdname, fd); - return qemuMonitorTextSendFileHandle(mon, fdname, fd); + if (mon->json) + ret = qemuMonitorJSONSendFileHandle(mon, fdname, fd); + else + ret = qemuMonitorTextSendFileHandle(mon, fdname, fd); + return ret; } int qemuMonitorCloseFileHandle(qemuMonitorPtr mon, const char *fdname) { + int ret; DEBUG("mon=%p, fd=%d fdname=%s", mon, mon->fd, fdname); - return qemuMonitorTextCloseFileHandle(mon, fdname); + if (mon->json) + ret = qemuMonitorJSONCloseFileHandle(mon, fdname); + else + ret = qemuMonitorTextCloseFileHandle(mon, fdname); + return ret; } int qemuMonitorAddHostNetwork(qemuMonitorPtr mon, const char *netstr) { + int ret; DEBUG("mon=%p, fd=%d netstr=%s", mon, mon->fd, netstr); - return qemuMonitorTextAddHostNetwork(mon, netstr); + if (mon->json) + ret = qemuMonitorJSONAddHostNetwork(mon, netstr); + else + ret = qemuMonitorTextAddHostNetwork(mon, netstr); + return ret; } @@ -902,8 +1065,13 @@ int qemuMonitorRemoveHostNetwork(qemuMonitorPtr mon, int vlan, const char *netname) { + int ret; DEBUG("mon=%p, fd=%d netname=%s", mon, mon->fd, netname); - return qemuMonitorTextRemoveHostNetwork(mon, vlan, netname); + if (mon->json) + ret = qemuMonitorJSONRemoveHostNetwork(mon, vlan, netname); + else + ret = qemuMonitorTextRemoveHostNetwork(mon, vlan, netname); + return ret; } diff --git a/src/qemu/qemu_monitor_json.c b/src/qemu/qemu_monitor_json.c new file mode 100644 index 0000000..95f74fa --- /dev/null +++ b/src/qemu/qemu_monitor_json.c @@ -0,0 +1,817 @@ +/* + * qemu_monitor_json.c: interaction with QEMU monitor console + * + * Copyright (C) 2006-2009 Red Hat, Inc. + * Copyright (C) 2006 Daniel P. Berrange + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Author: Daniel P. Berrange <berrange@redhat.com> + */ + +#include <config.h> + +#include <sys/types.h> +#include <sys/socket.h> +#include <sys/un.h> +#include <poll.h> +#include <unistd.h> +#include <string.h> + +#include "qemu_monitor_json.h" +#include "qemu_conf.h" +#include "memory.h" +#include "logging.h" +#include "driver.h" +#include "datatypes.h" +#include "virterror_internal.h" +#include "json.h" + +#define VIR_FROM_THIS VIR_FROM_QEMU + + +#define LINE_ENDING "\r\n" + +static int +qemuMonitorJSONIOProcessLine(qemuMonitorPtr mon ATTRIBUTE_UNUSED, + const char *line, + qemuMonitorMessagePtr msg) +{ + json_t *obj = NULL; + json_t *val; + int ret = -1; + + VIR_DEBUG("Line [%s]", line); + + if (json_parse_document(&obj, line) != JSON_OK) { + VIR_DEBUG0("Parsing JSON string failed"); + errno = EINVAL; + goto cleanup; + } + + if (obj->type != JSON_OBJECT) { + VIR_DEBUG0("Parsed JSON string isn't an object"); + errno = EINVAL; + } + + if (json_find_first_label(obj, "QMP", &val) == JSON_OK) { + VIR_DEBUG0("Got QMP capabilities data"); + ret = 0; + goto cleanup; + } + + if (json_find_first_label(obj, "event", &val) == JSON_OK) { + VIR_DEBUG0("Got an event"); + ret = 0; + goto cleanup; + } + + if (msg) { + msg->rxBuffer = strdup(line); + msg->rxLength = strlen(line); + msg->finished = 1; + } else { + VIR_DEBUG("Ignoring unexpected JSON message [%s]", line); + } + + ret = 0; + +cleanup: + if (obj) + json_free_value(&obj); + return ret; +} + +int qemuMonitorJSONIOProcess(qemuMonitorPtr mon, + const char *data, + size_t len, + qemuMonitorMessagePtr msg) +{ + int used = 0; + VIR_DEBUG("Data %d bytes [%s]", len, data); + + while (used < len) { + char *nl = strstr(data + used, LINE_ENDING); + + if (nl) { + int got = nl - (data + used); + char *line = strndup(data + used, got); + used += got + strlen(LINE_ENDING); + + if (qemuMonitorJSONIOProcessLine(mon, line, msg) < 0) { + VIR_FREE(line); + return -1; + } + + VIR_FREE(line); + } else { + break; + } + } + + VIR_DEBUG("Total used %d", used); + return used; +} + +static int +qemuMonitorCommandWithFd(qemuMonitorPtr mon, + json_t *cmd, + int scm_fd, + json_t **reply) +{ + int ret = -1; + qemuMonitorMessage msg; + char *cmdstr = NULL; + + *reply = NULL; + + memset(&msg, 0, sizeof msg); + + if (json_tree_to_string(cmd, &cmdstr) != JSON_OK) { + virReportOOMError(NULL); + goto cleanup; + } + if (virAsprintf(&msg.txBuffer, "%s\r\n", cmdstr) < 0) { + virReportOOMError(NULL); + goto cleanup; + } + msg.txLength = strlen(msg.txBuffer); + msg.txFD = scm_fd; + + VIR_DEBUG("Send command '%s' for write with FD %d", cmdstr, scm_fd); + + ret = qemuMonitorSend(mon, &msg); + + VIR_DEBUG("Receive command reply ret=%d errno=%d %d bytes '%s'", + ret, msg.lastErrno, msg.rxLength, msg.rxBuffer); + + + /* To make life safer for callers, already ensure there's at least an empty string */ + if (msg.rxBuffer && ret == 0) { + if (json_parse_document(reply, msg.rxBuffer) != JSON_OK) { + qemudReportError(NULL, NULL, NULL, VIR_ERR_INTERNAL_ERROR, + _("cannot parse JSON doc '%s'"), msg.rxBuffer); + goto cleanup; + } + } + + if (ret < 0) + virReportSystemError(NULL, msg.lastErrno, + _("cannot send monitor command '%s'"), cmdstr); + +cleanup: + VIR_FREE(cmdstr); + VIR_FREE(msg.txBuffer); + VIR_FREE(msg.rxBuffer); + + return ret; +} + + +static int +qemuMonitorCommand(qemuMonitorPtr mon, + json_t *cmd, + json_t **reply) { + return qemuMonitorCommandWithFd(mon, cmd, -1, reply); +} + + +static json_t * +qemuMonitorJSONMakeCommand(const char *cmdname, + ...) +{ + json_t *obj; + json_t *jargs = NULL; + va_list args; + char *key; + char *val; + + va_start(args, cmdname); + + if (!(obj = json_new_object())) + goto no_memory; + + if (json_insert_string_pair_into_object(obj, "execute", cmdname) != JSON_OK) + goto no_memory; + + while ((key = va_arg(args, char *)) != NULL) { + val = va_arg(args, char *); + if (!val) + goto error; + + if (!jargs && + !(jargs = json_new_object())) + goto no_memory; + + if (json_insert_string_pair_into_object(obj, key, val) != JSON_OK) + goto no_memory; + } + + if (jargs && + json_insert_pair_into_object(obj, "arguments", jargs) != JSON_OK) + goto no_memory; + + va_end(args); + + return obj; + +no_memory: + virReportOOMError(NULL); +error: + if (obj) + json_free_value(&obj); + if (jargs) + json_free_value(&jargs); + va_end(args); + return NULL; +} + + +int +qemuMonitorJSONStartCPUs(qemuMonitorPtr mon, + virConnectPtr conn ATTRIBUTE_UNUSED) +{ + int ret; + json_t *cmd = qemuMonitorJSONMakeCommand("cont", NULL); + json_t *reply = NULL; + if (!cmd) + return -1; + + ret = qemuMonitorCommand(mon, cmd, &reply); + + json_free_value(&cmd); + if (reply) + json_free_value(&reply); + return ret; +} + + +int +qemuMonitorJSONStopCPUs(qemuMonitorPtr mon) +{ + int ret; + json_t *cmd = qemuMonitorJSONMakeCommand("stop", NULL); + json_t *reply = NULL; + if (!cmd) + return -1; + + ret = qemuMonitorCommand(mon, cmd, &reply); + + json_free_value(&cmd); + if (reply) + json_free_value(&reply); + return ret; +} + + +int qemuMonitorJSONSystemPowerdown(qemuMonitorPtr mon) +{ + int ret; + json_t *cmd = qemuMonitorJSONMakeCommand("system_powerdown", NULL); + json_t *reply = NULL; + if (!cmd) + return -1; + + ret = qemuMonitorCommand(mon, cmd, &reply); + + json_free_value(&cmd); + if (reply) + json_free_value(&reply); + return ret; +} + + +int qemuMonitorJSONGetCPUInfo(qemuMonitorPtr mon, + int **pids) +{ + int ret; + json_t *cmd = qemuMonitorJSONMakeCommand("info", "type", "cpus", NULL); + json_t *reply = NULL; + + *pids = NULL; + + if (!cmd) + return -1; + + ret = qemuMonitorCommand(mon, cmd, &reply); + + json_free_value(&cmd); + if (reply) + json_free_value(&reply); + return ret; +} + + + +/* + * Returns: 0 if balloon not supported, +1 if balloon query worked + * or -1 on failure + */ +int qemuMonitorJSONGetBalloonInfo(qemuMonitorPtr mon, + unsigned long *currmem) +{ + int ret; + json_t *cmd = qemuMonitorJSONMakeCommand("info", "type", "balloon", NULL); + json_t *reply = NULL; + + *currmem = 0; + + if (!cmd) + return -1; + + ret = qemuMonitorCommand(mon, cmd, &reply); + + json_free_value(&cmd); + if (reply) + json_free_value(&reply); + return ret; +} + + +int qemuMonitorJSONGetBlockStatsInfo(qemuMonitorPtr mon, + const char *devname ATTRIBUTE_UNUSED, + long long *rd_req, + long long *rd_bytes, + long long *wr_req, + long long *wr_bytes, + long long *errs) +{ + int ret; + json_t *cmd = qemuMonitorJSONMakeCommand("info", "type", "blockstats", NULL); + json_t *reply = NULL; + + *rd_req = *rd_bytes = *wr_req = *wr_bytes = *errs = 0; + + if (!cmd) + return -1; + + ret = qemuMonitorCommand(mon, cmd, &reply); + + json_free_value(&cmd); + if (reply) + json_free_value(&reply); + return ret; +} + + +int qemuMonitorJSONSetVNCPassword(qemuMonitorPtr mon, + const char *password) +{ + int ret; + json_t *cmd = qemuMonitorJSONMakeCommand("change", "type", "vnc", "password", password, NULL); + json_t *reply = NULL; + if (!cmd) + return -1; + + ret = qemuMonitorCommand(mon, cmd, &reply); + + json_free_value(&cmd); + if (reply) + json_free_value(&reply); + return ret; +} + +/* + * Returns: 0 if balloon not supported, +1 if balloon adjust worked + * or -1 on failure + */ +int qemuMonitorJSONSetBalloon(qemuMonitorPtr mon, + unsigned long newmem ATTRIBUTE_UNUSED) +{ + int ret; + json_t *cmd = qemuMonitorJSONMakeCommand("balloon", NULL); + json_t *reply = NULL; + if (!cmd) + return -1; + + ret = qemuMonitorCommand(mon, cmd, &reply); + + json_free_value(&cmd); + if (reply) + json_free_value(&reply); + return ret; +} + +int qemuMonitorJSONEjectMedia(qemuMonitorPtr mon, + const char *devname) +{ + int ret; + json_t *cmd = qemuMonitorJSONMakeCommand("eject", "device", devname, NULL); + json_t *reply = NULL; + if (!cmd) + return -1; + + ret = qemuMonitorCommand(mon, cmd, &reply); + + json_free_value(&cmd); + if (reply) + json_free_value(&reply); + return ret; +} + + +int qemuMonitorJSONChangeMedia(qemuMonitorPtr mon, + const char *devname, + const char *newmedia) +{ + int ret; + json_t *cmd = qemuMonitorJSONMakeCommand("change", "device", devname, "media", newmedia, NULL); + json_t *reply = NULL; + if (!cmd) + return -1; + + ret = qemuMonitorCommand(mon, cmd, &reply); + + json_free_value(&cmd); + if (reply) + json_free_value(&reply); + return ret; +} + +static int qemuMonitorJSONSaveMemory(qemuMonitorPtr mon, + const char *cmdtype, + unsigned long long offset ATTRIBUTE_UNUSED, + size_t length ATTRIBUTE_UNUSED, + const char *path ATTRIBUTE_UNUSED) +{ + int ret; + json_t *cmd = qemuMonitorJSONMakeCommand(cmdtype, NULL); + json_t *reply = NULL; + if (!cmd) + return -1; + + ret = qemuMonitorCommand(mon, cmd, &reply); + + json_free_value(&cmd); + if (reply) + json_free_value(&reply); + return ret; +} + + +int qemuMonitorJSONSaveVirtualMemory(qemuMonitorPtr mon, + unsigned long long offset, + size_t length, + const char *path) +{ + return qemuMonitorJSONSaveMemory(mon, "memsave", offset, length, path); +} + +int qemuMonitorJSONSavePhysicalMemory(qemuMonitorPtr mon, + unsigned long long offset, + size_t length, + const char *path) +{ + return qemuMonitorJSONSaveMemory(mon, "pmemsave", offset, length, path); +} + + +int qemuMonitorJSONSetMigrationSpeed(qemuMonitorPtr mon, + unsigned long bandwidth ATTRIBUTE_UNUSED) +{ + int ret; + json_t *cmd = qemuMonitorJSONMakeCommand("migrate_set_speed", NULL); + json_t *reply = NULL; + if (!cmd) + return -1; + + ret = qemuMonitorCommand(mon, cmd, &reply); + + json_free_value(&cmd); + if (reply) + json_free_value(&reply); + return ret; +} + +int qemuMonitorJSONGetMigrationStatus(qemuMonitorPtr mon, + int *status, + unsigned long long *transferred, + unsigned long long *remaining, + unsigned long long *total) { + int ret; + json_t *cmd = qemuMonitorJSONMakeCommand("info migration", NULL); + json_t *reply = NULL; + + *status = 0; + *transferred = *remaining = *total = 0; + + if (!cmd) + return -1; + + ret = qemuMonitorCommand(mon, cmd, &reply); + + json_free_value(&cmd); + if (reply) + json_free_value(&reply); + return ret; +} + + +int qemuMonitorJSONMigrateToHost(qemuMonitorPtr mon, + int background ATTRIBUTE_UNUSED, + const char *hostname ATTRIBUTE_UNUSED, + int port ATTRIBUTE_UNUSED) +{ + int ret; + json_t *cmd = qemuMonitorJSONMakeCommand("migrate", NULL); + json_t *reply = NULL; + if (!cmd) + return -1; + + ret = qemuMonitorCommand(mon, cmd, &reply); + + json_free_value(&cmd); + if (reply) + json_free_value(&reply); + return ret; +} + + +int qemuMonitorJSONMigrateToCommand(qemuMonitorPtr mon, + int background ATTRIBUTE_UNUSED, + const char * const *argv ATTRIBUTE_UNUSED, + const char *target ATTRIBUTE_UNUSED) +{ + int ret; + json_t *cmd = qemuMonitorJSONMakeCommand("migrate", NULL); + json_t *reply = NULL; + if (!cmd) + return -1; + + ret = qemuMonitorCommand(mon, cmd, &reply); + + json_free_value(&cmd); + if (reply) + json_free_value(&reply); + return ret; +} + +int qemuMonitorJSONMigrateToUnix(qemuMonitorPtr mon, + int background ATTRIBUTE_UNUSED, + const char *unixfile ATTRIBUTE_UNUSED) +{ + int ret; + json_t *cmd = qemuMonitorJSONMakeCommand("migrate", NULL); + json_t *reply = NULL; + if (!cmd) + return -1; + + ret = qemuMonitorCommand(mon, cmd, &reply); + + json_free_value(&cmd); + if (reply) + json_free_value(&reply); + return ret; +} + +int qemuMonitorJSONMigrateCancel(qemuMonitorPtr mon) +{ + int ret; + json_t *cmd = qemuMonitorJSONMakeCommand("migrate", NULL); + json_t *reply = NULL; + if (!cmd) + return -1; + + ret = qemuMonitorCommand(mon, cmd, &reply); + + json_free_value(&cmd); + if (reply) + json_free_value(&reply); + return ret; +} + +int qemuMonitorJSONAddUSBDisk(qemuMonitorPtr mon, + const char *path) +{ + int ret; + json_t *cmd = qemuMonitorJSONMakeCommand("usb_add", "type", "disk", "path", path, NULL); + json_t *reply = NULL; + if (!cmd) + return -1; + + ret = qemuMonitorCommand(mon, cmd, &reply); + + json_free_value(&cmd); + if (reply) + json_free_value(&reply); + return ret; +} + + +int qemuMonitorJSONAddUSBDeviceExact(qemuMonitorPtr mon, + int bus ATTRIBUTE_UNUSED, + int dev ATTRIBUTE_UNUSED) +{ + int ret; + json_t *cmd = qemuMonitorJSONMakeCommand("usb_add", "type", "host", NULL); + json_t *reply = NULL; + if (!cmd) + return -1; + + ret = qemuMonitorCommand(mon, cmd, &reply); + + json_free_value(&cmd); + if (reply) + json_free_value(&reply); + return ret; + +} + +int qemuMonitorJSONAddUSBDeviceMatch(qemuMonitorPtr mon, + int vendor ATTRIBUTE_UNUSED, + int product ATTRIBUTE_UNUSED) +{ + int ret; + json_t *cmd = qemuMonitorJSONMakeCommand("usb_add", NULL); + json_t *reply = NULL; + if (!cmd) + return -1; + + ret = qemuMonitorCommand(mon, cmd, &reply); + + json_free_value(&cmd); + if (reply) + json_free_value(&reply); + return ret; +} + + +int qemuMonitorJSONAddPCIHostDevice(qemuMonitorPtr mon, + unsigned hostDomain ATTRIBUTE_UNUSED, + unsigned hostBus ATTRIBUTE_UNUSED, + unsigned hostSlot ATTRIBUTE_UNUSED, + unsigned hostFunction ATTRIBUTE_UNUSED, + unsigned *guestDomain, + unsigned *guestBus, + unsigned *guestSlot) +{ + int ret; + json_t *cmd = qemuMonitorJSONMakeCommand("pci_add", NULL); + json_t *reply = NULL; + + *guestDomain = *guestBus = *guestSlot = 0; + + if (!cmd) + return -1; + + ret = qemuMonitorCommand(mon, cmd, &reply); + + json_free_value(&cmd); + if (reply) + json_free_value(&reply); + return ret; +} + + +int qemuMonitorJSONAddPCIDisk(qemuMonitorPtr mon, + const char *path ATTRIBUTE_UNUSED, + const char *bus ATTRIBUTE_UNUSED, + unsigned *guestDomain, + unsigned *guestBus, + unsigned *guestSlot) { + int ret; + json_t *cmd = qemuMonitorJSONMakeCommand("pci_add", NULL); + json_t *reply = NULL; + + *guestDomain = *guestBus = *guestSlot = 0; + + if (!cmd) + return -1; + + ret = qemuMonitorCommand(mon, cmd, &reply); + + json_free_value(&cmd); + if (reply) + json_free_value(&reply); + return ret; +} + + +int qemuMonitorJSONAddPCINetwork(qemuMonitorPtr mon, + const char *nicstr ATTRIBUTE_UNUSED, + unsigned *guestDomain, + unsigned *guestBus, + unsigned *guestSlot) +{ + int ret; + json_t *cmd = qemuMonitorJSONMakeCommand("pci_add", NULL); + json_t *reply = NULL; + + *guestDomain = *guestBus = *guestSlot = 0; + + if (!cmd) + return -1; + + ret = qemuMonitorCommand(mon, cmd, &reply); + + json_free_value(&cmd); + if (reply) + json_free_value(&reply); + return ret; +} + + +int qemuMonitorJSONRemovePCIDevice(qemuMonitorPtr mon, + unsigned guestDomain ATTRIBUTE_UNUSED, + unsigned guestBus ATTRIBUTE_UNUSED, + unsigned guestSlot ATTRIBUTE_UNUSED) +{ + int ret; + json_t *cmd = qemuMonitorJSONMakeCommand("pci_del", NULL); + json_t *reply = NULL; + if (!cmd) + return -1; + + ret = qemuMonitorCommand(mon, cmd, &reply); + + json_free_value(&cmd); + if (reply) + json_free_value(&reply); + return ret; +} + + +int qemuMonitorJSONSendFileHandle(qemuMonitorPtr mon, + const char *fdname ATTRIBUTE_UNUSED, + int fd) +{ + int ret; + json_t *cmd = qemuMonitorJSONMakeCommand("getfd", NULL); + json_t *reply = NULL; + if (!cmd) + return -1; + + ret = qemuMonitorCommandWithFd(mon, cmd, fd, &reply); + + json_free_value(&cmd); + if (reply) + json_free_value(&reply); + return ret; +} + + +int qemuMonitorJSONCloseFileHandle(qemuMonitorPtr mon, + const char *fdname ATTRIBUTE_UNUSED) +{ + int ret; + json_t *cmd = qemuMonitorJSONMakeCommand("closefd", NULL); + json_t *reply = NULL; + if (!cmd) + return -1; + + ret = qemuMonitorCommand(mon, cmd, &reply); + + json_free_value(&cmd); + if (reply) + json_free_value(&reply); + return ret; +} + + +int qemuMonitorJSONAddHostNetwork(qemuMonitorPtr mon, + const char *netstr ATTRIBUTE_UNUSED) +{ + int ret; + json_t *cmd = qemuMonitorJSONMakeCommand("host_net_add", NULL); + json_t *reply = NULL; + if (!cmd) + return -1; + + ret = qemuMonitorCommand(mon, cmd, &reply); + + json_free_value(&cmd); + if (reply) + json_free_value(&reply); + return ret; +} + + +int qemuMonitorJSONRemoveHostNetwork(qemuMonitorPtr mon, + int vlan ATTRIBUTE_UNUSED, + const char *netname ATTRIBUTE_UNUSED) +{ + int ret; + json_t *cmd = qemuMonitorJSONMakeCommand("host_net_remove", NULL); + json_t *reply = NULL; + if (!cmd) + return -1; + + ret = qemuMonitorCommand(mon, cmd, &reply); + + json_free_value(&cmd); + if (reply) + json_free_value(&reply); + return ret; +} diff --git a/src/qemu/qemu_monitor_json.h b/src/qemu/qemu_monitor_json.h new file mode 100644 index 0000000..a44158a --- /dev/null +++ b/src/qemu/qemu_monitor_json.h @@ -0,0 +1,155 @@ +/* + * qemu_monitor_json.h: interaction with QEMU monitor console + * + * Copyright (C) 2006-2009 Red Hat, Inc. + * Copyright (C) 2006 Daniel P. Berrange + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Author: Daniel P. Berrange <berrange@redhat.com> + */ + + +#ifndef QEMU_MONITOR_JSON_H +#define QEMU_MONITOR_JSON_H + +#include "internal.h" + +#include "qemu_monitor.h" + +int qemuMonitorJSONIOProcess(qemuMonitorPtr mon, + const char *data, + size_t len, + qemuMonitorMessagePtr msg); + +int qemuMonitorJSONStartCPUs(qemuMonitorPtr mon, + virConnectPtr conn); +int qemuMonitorJSONStopCPUs(qemuMonitorPtr mon); + +int qemuMonitorJSONSystemPowerdown(qemuMonitorPtr mon); + +int qemuMonitorJSONGetCPUInfo(qemuMonitorPtr mon, + int **pids); +int qemuMonitorJSONGetBalloonInfo(qemuMonitorPtr mon, + unsigned long *currmem); +int qemuMonitorJSONGetBlockStatsInfo(qemuMonitorPtr mon, + const char *devname, + long long *rd_req, + long long *rd_bytes, + long long *wr_req, + long long *wr_bytes, + long long *errs); + + +int qemuMonitorJSONSetVNCPassword(qemuMonitorPtr mon, + const char *password); +int qemuMonitorJSONSetBalloon(qemuMonitorPtr mon, + unsigned long newmem); + +int qemuMonitorJSONEjectMedia(qemuMonitorPtr mon, + const char *devname); +int qemuMonitorJSONChangeMedia(qemuMonitorPtr mon, + const char *devname, + const char *newmedia); + + +int qemuMonitorJSONSaveVirtualMemory(qemuMonitorPtr mon, + unsigned long long offset, + size_t length, + const char *path); +int qemuMonitorJSONSavePhysicalMemory(qemuMonitorPtr mon, + unsigned long long offset, + size_t length, + const char *path); + +int qemuMonitorJSONSetMigrationSpeed(qemuMonitorPtr mon, + unsigned long bandwidth); + +int qemuMonitorJSONGetMigrationStatus(qemuMonitorPtr mon, + int *status, + unsigned long long *transferred, + unsigned long long *remaining, + unsigned long long *total); + +int qemuMonitorJSONMigrateToHost(qemuMonitorPtr mon, + int background, + const char *hostname, + int port); + +int qemuMonitorJSONMigrateToCommand(qemuMonitorPtr mon, + int background, + const char * const *argv, + const char *target); + +int qemuMonitorJSONMigrateToUnix(qemuMonitorPtr mon, + int background, + const char *unixfile); + +int qemuMonitorJSONMigrateCancel(qemuMonitorPtr mon); + +int qemuMonitorJSONAddUSBDisk(qemuMonitorPtr mon, + const char *path); + +int qemuMonitorJSONAddUSBDeviceExact(qemuMonitorPtr mon, + int bus, + int dev); +int qemuMonitorJSONAddUSBDeviceMatch(qemuMonitorPtr mon, + int vendor, + int product); + + +int qemuMonitorJSONAddPCIHostDevice(qemuMonitorPtr mon, + unsigned hostDomain, + unsigned hostBus, + unsigned hostSlot, + unsigned hostFunction, + unsigned *guestDomain, + unsigned *guestBus, + unsigned *guestSlot); + +int qemuMonitorJSONAddPCIDisk(qemuMonitorPtr mon, + const char *path, + const char *bus, + unsigned *guestDomain, + unsigned *guestBus, + unsigned *guestSlot); + +int qemuMonitorJSONAddPCINetwork(qemuMonitorPtr mon, + const char *nicstr, + unsigned *guestDomain, + unsigned *guestBus, + unsigned *guestSlot); + +int qemuMonitorJSONRemovePCIDevice(qemuMonitorPtr mon, + unsigned guestDomain, + unsigned guestBus, + unsigned guestSlot); + + +int qemuMonitorJSONSendFileHandle(qemuMonitorPtr mon, + const char *fdname, + int fd); + +int qemuMonitorJSONCloseFileHandle(qemuMonitorPtr mon, + const char *fdname); + +int qemuMonitorJSONAddHostNetwork(qemuMonitorPtr mon, + const char *netstr); + +int qemuMonitorJSONRemoveHostNetwork(qemuMonitorPtr mon, + int vlan, + const char *netname); + +#endif /* QEMU_MONITOR_JSON_H */ -- 1.6.2.5

On Tue, Nov 03, 2009 at 02:50:09PM -0500, Daniel P. Berrange wrote:
Initial support for the new QEMU monitor protocol using JSON as the data encoding format instead of plain text
* src/qemu/qemu_conf.c, src/qemu/qemu_conf.h: Hack to turn on QMP mode * src/qemu/qemu_monitor.c: Delegate to json monitor if enabled * src/qemu/qemu_monitor_json.c, src/qemu/qemu_monitor_json.h: Add impl of QMP protocol * src/Makefile.am: Add src/qemu/qemu_monitor_json.{c,h} --- po/POTFILES.in | 1 + src/Makefile.am | 2 + src/qemu/qemu_conf.c | 10 +- src/qemu/qemu_conf.h | 3 + src/qemu/qemu_monitor.c | 260 +++++++++++--- src/qemu/qemu_monitor_json.c | 817 ++++++++++++++++++++++++++++++++++++++++++ src/qemu/qemu_monitor_json.h | 155 ++++++++ 7 files changed, 1201 insertions(+), 47 deletions(-) create mode 100644 src/qemu/qemu_monitor_json.c create mode 100644 src/qemu/qemu_monitor_json.h
ACK, that was the goal of a lot of those patches ! thanks, Daniel -- Daniel Veillard | libxml Gnome XML XSLT toolkit http://xmlsoft.org/ daniel@veillard.com | Rpmfind RPM search engine http://rpmfind.net/ http://veillard.com/ | virtualization library http://libvirt.org/

Hello Daniel, I'm running cluster of libvirt-managed KVM's, and I'm having quite a lot of problems with libvirt concurrent access. It seems that this batch of patches might fix some of those, right? Should I test them or wait till next release before digging further into it and reporting? thanks a lot with best regards nik On Tue, Nov 03, 2009 at 02:49:54PM -0500, Daniel P. Berrange wrote:
This is an update of this series:
http://www.redhat.com/archives/libvir-list/2009-October/msg00644.html
minus the patches DV already ACKd which I've merged.
The main change in this series is that I've removed the change which added a RW-lock primitive in the QEMU driver. Instead I've implemented the job condition variable idea I described here
http://www.redhat.com/archives/libvir-list/2009-October/msg00815.html
The nice thing about this is that its actually possible to start doing timeouts of commands. This series only partially supports timeouts. A long running monitor command itself will not timeout, but if a second API call is made while someone else is using the monitor, this waiting call will timeout after 30 seconds if the monitor was not relased.
Daniel
-- Libvir-list mailing list Libvir-list@redhat.com https://www.redhat.com/mailman/listinfo/libvir-list
-- ------------------------------------- Nikola CIPRICH LinuxBox.cz, s.r.o. 28. rijna 168, 709 01 Ostrava tel.: +420 596 603 142 fax: +420 596 621 273 mobil: +420 777 093 799 www.linuxbox.cz mobil servis: +420 737 238 656 email servis: servis@linuxbox.cz -------------------------------------

On Wed, Nov 04, 2009 at 11:52:00AM +0100, Nikola Ciprich wrote:
Hello Daniel,
I'm running cluster of libvirt-managed KVM's, and I'm having quite a lot of problems with libvirt concurrent access. It seems that this batch of patches might fix some of those, right? Should I test them or wait till next release before digging further into it and reporting?
In general the earlier the better. But basically if we're commiting as much as possible by Friday and then enter a one week freeze then it may be a simpler to wait until this is applied and test either from git or from a snapshot starting this w.e. But in general don't hesitate to test early ! Daniel -- Daniel Veillard | libxml Gnome XML XSLT toolkit http://xmlsoft.org/ daniel@veillard.com | Rpmfind RPM search engine http://rpmfind.net/ http://veillard.com/ | virtualization library http://libvirt.org/
participants (3)
-
Daniel P. Berrange
-
Daniel Veillard
-
Nikola Ciprich