[libvirt] [PATCH 00/21] Rewrite the QEMU monitor handling

This patch series rewrites the QEMU monitor handling almost completely. The key theme here is to move from a totally synchronous way of interacting with the monitor, to a totally asynchronous way. This allows us to handle receipt & dispatch of asychronous events from QEMU. For example a notification of a disk-full error, or VM state change. In the process of doing this re-factoring I have also dropped in basic support/infrastructure for the JSON based monitor. The basic JSON parsing/formatting is working, but this needs to wait until QEMU community finalize their monitor syntax for the new commands & what args they accept. Daniel

The current virDomainObjListPtr object stores domain objects in an array. This means that to find a particular objects requires O(n) time, and more critically acquiring O(n) mutex locks. The new impl replaces the array with a virHashTable, keyed off UUID. Finding a object based on UUID is now O(1) time, and only requires a single mutex lock. Finding by name/id is unchanged in complexity. In changing this, all code which iterates over the array had to be updated to use a hash table iterator function callback. Several of the functions which were identically duplicating across all drivers were pulled into domain_conf.c * src/conf/domain_conf.h, src/conf/domain_conf.c: Change virDomainObjListPtr to use virHashTable. Add a initializer method virDomainObjListInit, and rename virDomainObjListFree to virDomainObjListDeinit, since its not actually freeing the container, only its contents. Also add some convenient methods virDomainObjListGetInactiveNames, virDomainObjListGetActiveIDs and virDomainObjListNumOfDomains which can be used to implement the correspondingly named public API entry points in drivers * src/libvirt_private.syms: Export new methods from domain_conf.h * src/lxc/lxc_driver.c, src/opennebula/one_driver.c, src/openvz/openvz_conf.c, src/openvz/openvz_driver.c, src/qemu/qemu_driver.c, src/test/test_driver.c, src/uml/uml_driver.c, src/vbox/vbox_tmpl.c: Update all code to deal with hash tables instead of arrays for domains --- src/conf/domain_conf.c | 274 ++++++++++++++++++++++++++++++------------- src/conf/domain_conf.h | 19 +++- src/libvirt_private.syms | 6 +- src/lxc/lxc_driver.c | 247 +++++++++++++++++---------------------- src/opennebula/one_driver.c | 68 +++-------- src/openvz/openvz_conf.c | 7 +- src/openvz/openvz_driver.c | 25 ++--- src/qemu/qemu_driver.c | 221 ++++++++++++++-------------------- src/test/test_driver.c | 60 +++------- src/uml/uml_driver.c | 125 +++++++++----------- src/vbox/vbox_tmpl.c | 2 - 11 files changed, 519 insertions(+), 535 deletions(-) diff --git a/src/conf/domain_conf.c b/src/conf/domain_conf.c index 7d558e8..de07e13 100644 --- a/src/conf/domain_conf.c +++ b/src/conf/domain_conf.c @@ -205,51 +205,93 @@ VIR_ENUM_IMPL(virDomainSeclabel, VIR_DOMAIN_SECLABEL_LAST, #ifndef PROXY -virDomainObjPtr virDomainFindByID(const virDomainObjListPtr doms, - int id) +int virDomainObjListInit(virDomainObjListPtr doms) { - unsigned int i; - - for (i = 0 ; i < doms->count ; i++) { - virDomainObjLock(doms->objs[i]); - if (virDomainIsActive(doms->objs[i]) && - doms->objs[i]->def->id == id) - return doms->objs[i]; - virDomainObjUnlock(doms->objs[i]); + doms->objs = virHashCreate(50); + if (!doms->objs) { + virReportOOMError(NULL); + return -1; } + return 0; +} - return NULL; + +static void virDomainObjListDeallocator(void *payload, const char *name ATTRIBUTE_UNUSED) +{ + virDomainObjPtr obj = payload; + virDomainObjFree(obj); +} + +void virDomainObjListDeinit(virDomainObjListPtr doms) +{ + if (doms->objs) + virHashFree(doms->objs, virDomainObjListDeallocator); +} + + +static int virDomainObjListSearchID(const void *payload, + const char *name ATTRIBUTE_UNUSED, + const void *data) +{ + virDomainObjPtr obj = (virDomainObjPtr)payload; + const int *id = data; + int want = 0; + + virDomainObjLock(obj); + if (virDomainIsActive(obj) && + obj->def->id == *id) + want = 1; + virDomainObjUnlock(obj); + return want; +} + +virDomainObjPtr virDomainFindByID(const virDomainObjListPtr doms, + int id) +{ + virDomainObjPtr obj; + obj = virHashSearch(doms->objs, virDomainObjListSearchID, &id); + if (obj) + virDomainObjLock(obj); + return obj; } virDomainObjPtr virDomainFindByUUID(const virDomainObjListPtr doms, const unsigned char *uuid) { - unsigned int i; + char uuidstr[VIR_UUID_STRING_BUFLEN]; + virDomainObjPtr obj; - for (i = 0 ; i < doms->count ; i++) { - virDomainObjLock(doms->objs[i]); - if (!memcmp(doms->objs[i]->def->uuid, uuid, VIR_UUID_BUFLEN)) - return doms->objs[i]; - virDomainObjUnlock(doms->objs[i]); - } + virUUIDFormat(uuid, uuidstr); - return NULL; + obj = virHashLookup(doms->objs, uuidstr); + if (obj) + virDomainObjLock(obj); + return obj; +} + +static int virDomainObjListSearchName(const void *payload, + const char *name ATTRIBUTE_UNUSED, + const void *data) +{ + virDomainObjPtr obj = (virDomainObjPtr)payload; + int want = 0; + + virDomainObjLock(obj); + if (STREQ(obj->def->name, (const char *)data)) + want = 1; + virDomainObjUnlock(obj); + return want; } virDomainObjPtr virDomainFindByName(const virDomainObjListPtr doms, const char *name) { - unsigned int i; - - for (i = 0 ; i < doms->count ; i++) { - virDomainObjLock(doms->objs[i]); - if (STREQ(doms->objs[i]->def->name, name)) - return doms->objs[i]; - virDomainObjUnlock(doms->objs[i]); - } - - return NULL; + virDomainObjPtr obj; + obj = virHashSearch(doms->objs, virDomainObjListSearchName, name); + if (obj) + virDomainObjLock(obj); + return obj; } #endif /* !PROXY */ @@ -557,21 +599,6 @@ void virDomainObjFree(virDomainObjPtr dom) VIR_FREE(dom); } -void virDomainObjListFree(virDomainObjListPtr vms) -{ - unsigned int i; - - if (!vms) - return; - - for (i = 0 ; i < vms->count ; i++) - virDomainObjFree(vms->objs[i]); - - VIR_FREE(vms->objs); - vms->count = 0; -} - - static virDomainObjPtr virDomainObjNew(virConnectPtr conn) { virDomainObjPtr domain; @@ -601,6 +628,7 @@ virDomainObjPtr virDomainAssignDef(virConnectPtr conn, const virDomainDefPtr def) { virDomainObjPtr domain; + char uuidstr[VIR_UUID_STRING_BUFLEN]; if ((domain = virDomainFindByUUID(doms, def->uuid))) { if (!virDomainIsActive(domain)) { @@ -615,49 +643,34 @@ virDomainObjPtr virDomainAssignDef(virConnectPtr conn, return domain; } - if (VIR_REALLOC_N(doms->objs, doms->count + 1) < 0) { - virReportOOMError(conn); - return NULL; - } - if (!(domain = virDomainObjNew(conn))) return NULL; - domain->def = def; - doms->objs[doms->count] = domain; - doms->count++; + virUUIDFormat(def->uuid, uuidstr); + if (virHashAddEntry(doms->objs, uuidstr, domain) < 0) { + VIR_FREE(domain); + virReportOOMError(conn); + return NULL; + } return domain; } +/* + * The caller must hold a lock on the driver owning 'doms', + * and must also have locked 'dom', to ensure no one else + * is either waiting for 'dom' or still usingn it + */ void virDomainRemoveInactive(virDomainObjListPtr doms, virDomainObjPtr dom) { - unsigned int i; + char uuidstr[VIR_UUID_STRING_BUFLEN]; + virUUIDFormat(dom->def->uuid, uuidstr); virDomainObjUnlock(dom); - for (i = 0 ; i < doms->count ; i++) { - virDomainObjLock(doms->objs[i]); - if (doms->objs[i] == dom) { - virDomainObjUnlock(doms->objs[i]); - virDomainObjFree(doms->objs[i]); - - if (i < (doms->count - 1)) - memmove(doms->objs + i, doms->objs + i + 1, - sizeof(*(doms->objs)) * (doms->count - (i + 1))); - - if (VIR_REALLOC_N(doms->objs, doms->count - 1) < 0) { - ; /* Failure to reduce memory allocation isn't fatal */ - } - doms->count--; - - break; - } - virDomainObjUnlock(doms->objs[i]); - } - + virHashRemoveEntry(doms->objs, uuidstr, virDomainObjListDeallocator); } @@ -4784,7 +4797,7 @@ static virDomainObjPtr virDomainLoadStatus(virConnectPtr conn, { char *statusFile = NULL; virDomainObjPtr obj = NULL; - virDomainObjPtr tmp = NULL; + char uuidstr[VIR_UUID_STRING_BUFLEN]; if ((statusFile = virDomainConfigFile(conn, statusDir, name)) == NULL) goto error; @@ -4792,23 +4805,20 @@ static virDomainObjPtr virDomainLoadStatus(virConnectPtr conn, if (!(obj = virDomainObjParseFile(conn, caps, statusFile))) goto error; - tmp = virDomainFindByName(doms, obj->def->name); - if (tmp) { - virDomainObjUnlock(obj); + virUUIDFormat(obj->def->uuid, uuidstr); + + if (virHashLookup(doms->objs, uuidstr) != NULL) { virDomainReportError(conn, VIR_ERR_INTERNAL_ERROR, _("unexpected domain %s already exists"), obj->def->name); goto error; } - if (VIR_REALLOC_N(doms->objs, doms->count + 1) < 0) { + if (virHashAddEntry(doms->objs, uuidstr, obj) < 0) { virReportOOMError(conn); goto error; } - doms->objs[doms->count] = obj; - doms->count++; - if (notify) (*notify)(obj, 1, opaque); @@ -4995,4 +5005,106 @@ void virDomainObjUnlock(virDomainObjPtr obj) virMutexUnlock(&obj->lock); } + +static void virDomainObjListCountActive(void *payload, const char *name ATTRIBUTE_UNUSED, void *data) +{ + virDomainObjPtr obj = payload; + int *count = data; + virDomainObjLock(obj); + if (virDomainIsActive(obj)) + (*count)++; + virDomainObjUnlock(obj); +} + +static void virDomainObjListCountInactive(void *payload, const char *name ATTRIBUTE_UNUSED, void *data) +{ + virDomainObjPtr obj = payload; + int *count = data; + virDomainObjLock(obj); + if (!virDomainIsActive(obj)) + (*count)++; + virDomainObjUnlock(obj); +} + +int virDomainObjListNumOfDomains(virDomainObjListPtr doms, int active) +{ + int count = 0; + if (active) + virHashForEach(doms->objs, virDomainObjListCountActive, &count); + else + virHashForEach(doms->objs, virDomainObjListCountInactive, &count); + return count; +} + +struct virDomainIDData { + int numids; + int maxids; + int *ids; +}; + +static void virDomainObjListCopyActiveIDs(void *payload, const char *name ATTRIBUTE_UNUSED, void *opaque) +{ + virDomainObjPtr obj = payload; + struct virDomainIDData *data = opaque; + virDomainObjLock(obj); + if (virDomainIsActive(obj) && data->numids < data->maxids) + data->ids[data->numids++] = obj->def->id; + virDomainObjUnlock(obj); +} + +int virDomainObjListGetActiveIDs(virDomainObjListPtr doms, + int *ids, + int maxids) +{ + struct virDomainIDData data = { 0, maxids, ids }; + virHashForEach(doms->objs, virDomainObjListCopyActiveIDs, &data); + return data.numids; +} + +struct virDomainNameData { + int oom; + int numnames; + int maxnames; + char **const names; +}; + +static void virDomainObjListCopyInactiveNames(void *payload, const char *name ATTRIBUTE_UNUSED, void *opaque) +{ + virDomainObjPtr obj = payload; + struct virDomainNameData *data = opaque; + + if (data->oom) + return; + + virDomainObjLock(obj); + if (!virDomainIsActive(obj) && data->numnames < data->maxnames) { + if (!(data->names[data->numnames] = strdup(obj->def->name))) + data->oom = 1; + else + data->numnames++; + } + virDomainObjUnlock(obj); +} + + +int virDomainObjListGetInactiveNames(virDomainObjListPtr doms, + char **const names, + int maxnames) +{ + struct virDomainNameData data = { 0, 0, maxnames, names }; + int i; + virHashForEach(doms->objs, virDomainObjListCopyInactiveNames, &data); + if (data.oom) { + virReportOOMError(NULL); + goto cleanup; + } + + return data.numnames; + +cleanup: + for (i = 0 ; i < data.numnames ; i++) + VIR_FREE(data.names[i]); + return -1; +} + #endif /* ! PROXY */ diff --git a/src/conf/domain_conf.h b/src/conf/domain_conf.h index ff1b0cf..389e259 100644 --- a/src/conf/domain_conf.h +++ b/src/conf/domain_conf.h @@ -33,6 +33,7 @@ #include "storage_encryption_conf.h" #include "util.h" #include "threads.h" +#include "hash.h" /* Private component of virDomainXMLFlags */ typedef enum { @@ -640,8 +641,9 @@ struct _virDomainObj { typedef struct _virDomainObjList virDomainObjList; typedef virDomainObjList *virDomainObjListPtr; struct _virDomainObjList { - unsigned int count; - virDomainObjPtr *objs; + /* uuid string -> virDomainObj mapping + * for O(1), lockless lookup-by-uuid */ + virHashTable *objs; }; static inline int @@ -650,6 +652,8 @@ virDomainIsActive(virDomainObjPtr dom) return dom->def->id != -1; } +int virDomainObjListInit(virDomainObjListPtr objs); +void virDomainObjListDeinit(virDomainObjListPtr objs); virDomainObjPtr virDomainFindByID(const virDomainObjListPtr doms, int id); @@ -672,7 +676,6 @@ void virDomainHostdevDefFree(virDomainHostdevDefPtr def); void virDomainDeviceDefFree(virDomainDeviceDefPtr def); void virDomainDefFree(virDomainDefPtr vm); void virDomainObjFree(virDomainObjPtr vm); -void virDomainObjListFree(virDomainObjListPtr vms); virDomainObjPtr virDomainAssignDef(virConnectPtr conn, virDomainObjListPtr doms, @@ -784,6 +787,16 @@ int virDomainVideoDefaultRAM(virDomainDefPtr def, int type); void virDomainObjLock(virDomainObjPtr obj); void virDomainObjUnlock(virDomainObjPtr obj); +int virDomainObjListNumOfDomains(virDomainObjListPtr doms, int active); + +int virDomainObjListGetActiveIDs(virDomainObjListPtr doms, + int *ids, + int maxids); +int virDomainObjListGetInactiveNames(virDomainObjListPtr doms, + char **const names, + int maxnames); + + VIR_ENUM_DECL(virDomainVirt) VIR_ENUM_DECL(virDomainBoot) VIR_ENUM_DECL(virDomainFeature) diff --git a/src/libvirt_private.syms b/src/libvirt_private.syms index 98ea7f8..bd9d84a 100644 --- a/src/libvirt_private.syms +++ b/src/libvirt_private.syms @@ -124,7 +124,6 @@ virDomainLoadAllConfigs; virDomainNetDefFree; virDomainNetTypeToString; virDomainObjFree; -virDomainObjListFree; virDomainRemoveInactive; virDomainSaveXML; virDomainSaveConfig; @@ -147,6 +146,11 @@ virDomainObjLock; virDomainObjUnlock; virDomainStateTypeToString; virDomainStateTypeFromString; +virDomainObjListGetInactiveNames; +virDomainObjListGetActiveIDs; +virDomainObjListNumOfDomains; +virDomainObjListInit; +virDomainObjListDeinit; # domain_event.h diff --git a/src/lxc/lxc_driver.c b/src/lxc/lxc_driver.c index ef97364..4f0787b 100644 --- a/src/lxc/lxc_driver.c +++ b/src/lxc/lxc_driver.c @@ -220,31 +220,21 @@ cleanup: static int lxcListDomains(virConnectPtr conn, int *ids, int nids) { lxc_driver_t *driver = conn->privateData; - int got = 0, i; + int n; lxcDriverLock(driver); - for (i = 0 ; i < driver->domains.count && got < nids ; i++) { - virDomainObjLock(driver->domains.objs[i]); - if (virDomainIsActive(driver->domains.objs[i])) - ids[got++] = driver->domains.objs[i]->def->id; - virDomainObjUnlock(driver->domains.objs[i]); - } + n = virDomainObjListGetActiveIDs(&driver->domains, ids, nids); lxcDriverUnlock(driver); - return got; + return n; } static int lxcNumDomains(virConnectPtr conn) { lxc_driver_t *driver = conn->privateData; - int n = 0, i; + int n; lxcDriverLock(driver); - for (i = 0 ; i < driver->domains.count ; i++) { - virDomainObjLock(driver->domains.objs[i]); - if (virDomainIsActive(driver->domains.objs[i])) - n++; - virDomainObjUnlock(driver->domains.objs[i]); - } + n = virDomainObjListNumOfDomains(&driver->domains, 1); lxcDriverUnlock(driver); return n; @@ -253,43 +243,22 @@ static int lxcNumDomains(virConnectPtr conn) { static int lxcListDefinedDomains(virConnectPtr conn, char **const names, int nnames) { lxc_driver_t *driver = conn->privateData; - int got = 0, i; + int n; lxcDriverLock(driver); - for (i = 0 ; i < driver->domains.count && got < nnames ; i++) { - virDomainObjLock(driver->domains.objs[i]); - if (!virDomainIsActive(driver->domains.objs[i])) { - if (!(names[got++] = strdup(driver->domains.objs[i]->def->name))) { - virReportOOMError(conn); - virDomainObjUnlock(driver->domains.objs[i]); - goto cleanup; - } - } - virDomainObjUnlock(driver->domains.objs[i]); - } + n = virDomainObjListGetInactiveNames(&driver->domains, names, nnames); lxcDriverUnlock(driver); - return got; - - cleanup: - for (i = 0 ; i < got ; i++) - VIR_FREE(names[i]); - lxcDriverUnlock(driver); - return -1; + return n; } static int lxcNumDefinedDomains(virConnectPtr conn) { lxc_driver_t *driver = conn->privateData; - int n = 0, i; + int n; lxcDriverLock(driver); - for (i = 0 ; i < driver->domains.count ; i++) { - virDomainObjLock(driver->domains.objs[i]); - if (!virDomainIsActive(driver->domains.objs[i])) - n++; - virDomainObjUnlock(driver->domains.objs[i]); - } + n = virDomainObjListNumOfDomains(&driver->domains, 0); lxcDriverUnlock(driver); return n; @@ -898,27 +867,15 @@ static void lxcMonitorEvent(int watch, int events ATTRIBUTE_UNUSED, void *data) { - lxc_driver_t *driver = data; - virDomainObjPtr vm = NULL; + lxc_driver_t *driver = lxc_driver; + virDomainObjPtr vm = data; virDomainEventPtr event = NULL; - unsigned int i; lxcDriverLock(driver); - for (i = 0 ; i < driver->domains.count ; i++) { - virDomainObjPtr tmpvm = driver->domains.objs[i]; - virDomainObjLock(tmpvm); - if (tmpvm->monitorWatch == watch) { - vm = tmpvm; - break; - } - virDomainObjUnlock(tmpvm); - } - if (!vm) { - virEventRemoveHandle(watch); - goto cleanup; - } + virDomainObjLock(vm); + lxcDriverUnlock(driver); - if (vm->monitor != fd) { + if (vm->monitor != fd || vm->monitorWatch != watch) { virEventRemoveHandle(watch); goto cleanup; } @@ -936,11 +893,12 @@ static void lxcMonitorEvent(int watch, } cleanup: - if (vm) - virDomainObjUnlock(vm); - if (event) + virDomainObjUnlock(vm); + if (event) { + lxcDriverLock(driver); lxcDomainEventQueue(driver, event); - lxcDriverUnlock(driver); + lxcDriverUnlock(driver); + } } @@ -1572,9 +1530,40 @@ static int lxcCheckNetNsSupport(void) } +struct lxcAutostartData { + lxc_driver_t *driver; + virConnectPtr conn; +}; + +static void +lxcAutostartDomain(void *payload, const char *name ATTRIBUTE_UNUSED, void *opaque) +{ + virDomainObjPtr vm = payload; + const struct lxcAutostartData *data = opaque; + + virDomainObjLock(vm); + if (vm->autostart && + !virDomainIsActive(vm)) { + int ret = lxcVmStart(data->conn, data->driver, vm); + if (ret < 0) { + virErrorPtr err = virGetLastError(); + VIR_ERROR(_("Failed to autostart VM '%s': %s\n"), + vm->def->name, + err ? err->message : ""); + } else { + virDomainEventPtr event = + virDomainEventNewFromObj(vm, + VIR_DOMAIN_EVENT_STARTED, + VIR_DOMAIN_EVENT_STARTED_BOOTED); + if (event) + lxcDomainEventQueue(data->driver, event); + } + } + virDomainObjUnlock(vm); +} + static void lxcAutostartConfigs(lxc_driver_t *driver) { - unsigned int i; /* XXX: Figure out a better way todo this. The domain * startup code needs a connection handle in order * to lookup the bridge associated with a virtual @@ -1583,39 +1572,65 @@ lxcAutostartConfigs(lxc_driver_t *driver) { virConnectPtr conn = virConnectOpen("lxc:///"); /* Ignoring NULL conn which is mostly harmless here */ + struct lxcAutostartData data = { driver, conn }; + lxcDriverLock(driver); - for (i = 0 ; i < driver->domains.count ; i++) { - virDomainObjPtr vm = driver->domains.objs[i]; - virDomainObjLock(vm); - if (vm->autostart && - !virDomainIsActive(vm)) { - int ret = lxcVmStart(conn, driver, vm); - if (ret < 0) { - virErrorPtr err = virGetLastError(); - VIR_ERROR(_("Failed to autostart VM '%s': %s\n"), - vm->def->name, - err ? err->message : ""); - } else { - virDomainEventPtr event = - virDomainEventNewFromObj(vm, - VIR_DOMAIN_EVENT_STARTED, - VIR_DOMAIN_EVENT_STARTED_BOOTED); - if (event) - lxcDomainEventQueue(driver, event); - } - } - virDomainObjUnlock(vm); - } + virHashForEach(driver->domains.objs, lxcAutostartDomain, &data); lxcDriverUnlock(driver); if (conn) virConnectClose(conn); } +static void +lxcReconnectVM(void *payload, const char *name ATTRIBUTE_UNUSED, void *opaque) +{ + virDomainObjPtr vm = payload; + lxc_driver_t *driver = opaque; + char *config = NULL; + virDomainDefPtr tmp; + + virDomainObjLock(vm); + if ((vm->monitor = lxcMonitorClient(NULL, driver, vm)) < 0) { + goto cleanup; + } + + /* Read pid from controller */ + if ((virFileReadPid(lxc_driver->stateDir, vm->def->name, &vm->pid)) != 0) { + close(vm->monitor); + vm->monitor = -1; + goto cleanup; + } + + if ((config = virDomainConfigFile(NULL, + driver->stateDir, + vm->def->name)) == NULL) + goto cleanup; + + /* Try and load the live config */ + tmp = virDomainDefParseFile(NULL, driver->caps, config, 0); + VIR_FREE(config); + if (tmp) { + vm->newDef = vm->def; + vm->def = tmp; + } + + if (vm->pid != 0) { + vm->def->id = vm->pid; + vm->state = VIR_DOMAIN_RUNNING; + } else { + vm->def->id = -1; + close(vm->monitor); + vm->monitor = -1; + } + +cleanup: + virDomainObjUnlock(vm); +} + static int lxcStartup(int privileged) { - unsigned int i; char *ld; int rc; @@ -1647,6 +1662,9 @@ static int lxcStartup(int privileged) goto cleanup; } + if (virDomainObjListInit(&lxc_driver->domains) < 0) + goto cleanup; + if (VIR_ALLOC(lxc_driver->domainEventCallbacks) < 0) goto cleanup; if (!(lxc_driver->domainEventQueue = virDomainEventQueueNew())) @@ -1681,50 +1699,7 @@ static int lxcStartup(int privileged) 0, NULL, NULL) < 0) goto cleanup; - for (i = 0 ; i < lxc_driver->domains.count ; i++) { - virDomainObjPtr vm = lxc_driver->domains.objs[i]; - char *config = NULL; - virDomainDefPtr tmp; - - virDomainObjLock(vm); - if ((vm->monitor = lxcMonitorClient(NULL, lxc_driver, vm)) < 0) { - virDomainObjUnlock(vm); - continue; - } - - /* Read pid from controller */ - if ((rc = virFileReadPid(lxc_driver->stateDir, vm->def->name, &vm->pid)) != 0) { - close(vm->monitor); - vm->monitor = -1; - virDomainObjUnlock(vm); - continue; - } - - if ((config = virDomainConfigFile(NULL, - lxc_driver->stateDir, - vm->def->name)) == NULL) { - virDomainObjUnlock(vm); - continue; - } - - /* Try and load the live config */ - tmp = virDomainDefParseFile(NULL, lxc_driver->caps, config, 0); - VIR_FREE(config); - if (tmp) { - vm->newDef = vm->def; - vm->def = tmp; - } - - if (vm->pid != 0) { - vm->def->id = vm->pid; - vm->state = VIR_DOMAIN_RUNNING; - } else { - vm->def->id = -1; - close(vm->monitor); - vm->monitor = -1; - } - virDomainObjUnlock(vm); - } + virHashForEach(lxc_driver->domains.objs, lxcReconnectVM, lxc_driver); lxcDriverUnlock(lxc_driver); return 0; @@ -1780,7 +1755,7 @@ static int lxcShutdown(void) return(-1); lxcDriverLock(lxc_driver); - virDomainObjListFree(&lxc_driver->domains); + virDomainObjListDeinit(&lxc_driver->domains); virDomainEventCallbackListFree(lxc_driver->domainEventCallbacks); virDomainEventQueueFree(lxc_driver->domainEventQueue); @@ -1809,19 +1784,13 @@ static int lxcShutdown(void) */ static int lxcActive(void) { - unsigned int i; - int active = 0; + int active; if (lxc_driver == NULL) return(0); lxcDriverLock(lxc_driver); - for (i = 0 ; i < lxc_driver->domains.count ; i++) { - virDomainObjLock(lxc_driver->domains.objs[i]); - if (virDomainIsActive(lxc_driver->domains.objs[i])) - active = 1; - virDomainObjUnlock(lxc_driver->domains.objs[i]); - } + active = virDomainObjListNumOfDomains(&lxc_driver->domains, 1); lxcDriverUnlock(lxc_driver); return active; diff --git a/src/opennebula/one_driver.c b/src/opennebula/one_driver.c index 9bcd5c3..0b807ad 100644 --- a/src/opennebula/one_driver.c +++ b/src/opennebula/one_driver.c @@ -178,32 +178,22 @@ return_point: static int oneListDomains(virConnectPtr conn, int *ids, int nids) { one_driver_t *driver = (one_driver_t *)conn->privateData; - int got = 0, i; + int n; oneDriverLock(driver); - for (i = 0 ; i < driver->domains.count && got < nids ; i++){ - virDomainObjLock(driver->domains.objs[i]); - if (virDomainIsActive(driver->domains.objs[i])) - ids[got++] = driver->domains.objs[i]->def->id; - virDomainObjUnlock(driver->domains.objs[i]); - } + n = virDomainObjListGetActiveIDs(&driver->domains, ids, nids); oneDriverUnlock(driver); - return got; + return n; } static int oneNumDomains(virConnectPtr conn) { one_driver_t *driver = (one_driver_t *)conn->privateData; - int n = 0, i; + int n; oneDriverLock(driver); - for (i = 0 ; i < driver->domains.count ; i++){ - virDomainObjLock(driver->domains.objs[i]); - if (virDomainIsActive(driver->domains.objs[i])) - n++; - virDomainObjUnlock(driver->domains.objs[i]); - } + n = virDomainObjListNumOfDomains(&driver->domains, 1); oneDriverUnlock(driver); return n; @@ -212,44 +202,22 @@ static int oneNumDomains(virConnectPtr conn) static int oneListDefinedDomains(virConnectPtr conn, char **const names, int nnames) { one_driver_t *driver = (one_driver_t *)conn->privateData; - int got = 0, i; + int n; oneDriverLock(driver); - for (i = 0 ; i < driver->domains.count && got < nnames ; i++) { - virDomainObjLock(driver->domains.objs[i]); - if (!virDomainIsActive(driver->domains.objs[i])) { - if (!(names[got++] = strdup(driver->domains.objs[i]->def->name))) { - virReportOOMError(conn); - virDomainObjUnlock(driver->domains.objs[i]); - goto cleanup; - } - } - virDomainObjUnlock(driver->domains.objs[i]); - } + n = virDomainObjListGetInactiveNames(&driver->domains, names, nnames); oneDriverUnlock(driver); - return got; - -cleanup: - for (i = 0 ; i < got ; i++) - VIR_FREE(names[i]); - oneDriverUnlock(driver); - - return -1; + return n; } static int oneNumDefinedDomains(virConnectPtr conn) { one_driver_t *driver = (one_driver_t *)conn->privateData; - int n = 0, i; + int n; oneDriverLock(driver); - for (i = 0 ; i < driver->domains.count ; i++){ - virDomainObjLock(driver->domains.objs[i]); - if (!virDomainIsActive(driver->domains.objs[i])) - n++; - virDomainObjUnlock(driver->domains.objs[i]); - } + n = virDomainObjListNumOfDomains(&driver->domains, 0); oneDriverUnlock(driver); return n; @@ -647,6 +615,12 @@ static int oneStartup(int privileged ATTRIBUTE_UNUSED){ return -1; } + if (virDomainObjListInit(&one_driver->domains) < 0) { + virMutexDestroy(&one_driver->lock); + VIR_FREE(one_driver); + return -1; + } + c_oneStart(); oneDriverLock(one_driver); one_driver->nextid=1; @@ -665,7 +639,7 @@ static int oneShutdown(void){ return(-1); oneDriverLock(one_driver); - virDomainObjListFree(&one_driver->domains); + virDomainObjListDeinit(&one_driver->domains); virCapabilitiesFree(one_driver->caps); oneDriverUnlock(one_driver); @@ -677,19 +651,13 @@ static int oneShutdown(void){ } static int oneActive(void){ - unsigned int i; int active = 0; if (one_driver == NULL) return(0); oneDriverLock(one_driver); - for (i = 0 ; i < one_driver->domains.count ; i++) { - virDomainObjLock(one_driver->domains.objs[i]); - if (virDomainIsActive(one_driver->domains.objs[i])) - active = 1; - virDomainObjUnlock(one_driver->domains.objs[i]); - } + active = virDomainObjListNumOfDomains(&one_driver->domains, 1); oneDriverUnlock(one_driver); return active; diff --git a/src/openvz/openvz_conf.c b/src/openvz/openvz_conf.c index 403f537..c928afb 100644 --- a/src/openvz/openvz_conf.c +++ b/src/openvz/openvz_conf.c @@ -421,7 +421,7 @@ openvzFreeDriver(struct openvz_driver *driver) if (!driver) return; - virDomainObjListFree(&driver->domains); + virDomainObjListDeinit(&driver->domains); virCapabilitiesFree(driver->caps); } @@ -509,11 +509,10 @@ int openvzLoadDomains(struct openvz_driver *driver) { openvzReadNetworkConf(NULL, dom->def, veid); openvzReadFSConf(NULL, dom->def, veid); - if (VIR_REALLOC_N(driver->domains.objs, - driver->domains.count + 1) < 0) + virUUIDFormat(dom->def->uuid, uuidstr); + if (virHashAddEntry(driver->domains.objs, uuidstr, dom) < 0) goto no_memory; - driver->domains.objs[driver->domains.count++] = dom; dom = NULL; } diff --git a/src/openvz/openvz_driver.c b/src/openvz/openvz_driver.c index f64ad1e..b0092cd 100644 --- a/src/openvz/openvz_driver.c +++ b/src/openvz/openvz_driver.c @@ -1168,6 +1168,9 @@ static virDrvOpenStatus openvzOpen(virConnectPtr conn, return VIR_DRV_OPEN_ERROR; } + if (virDomainObjListInit(&driver->domains) < 0) + goto cleanup; + if (!(driver->caps = openvzCapsInit())) goto cleanup; @@ -1247,18 +1250,13 @@ static int openvzListDomains(virConnectPtr conn, int *ids, int nids) { static int openvzNumDomains(virConnectPtr conn) { struct openvz_driver *driver = conn->privateData; - int nactive = 0, i; + int n; openvzDriverLock(driver); - for (i = 0 ; i < driver->domains.count ; i++) { - virDomainObjLock(driver->domains.objs[i]); - if (virDomainIsActive(driver->domains.objs[i])) - nactive++; - virDomainObjUnlock(driver->domains.objs[i]); - } + n = virDomainObjListNumOfDomains(&driver->domains, 1); openvzDriverUnlock(driver); - return nactive; + return n; } static int openvzListDefinedDomains(virConnectPtr conn, @@ -1350,18 +1348,13 @@ Version: 2.2 static int openvzNumDefinedDomains(virConnectPtr conn) { struct openvz_driver *driver = conn->privateData; - int ninactive = 0, i; + int n; openvzDriverLock(driver); - for (i = 0 ; i < driver->domains.count ; i++) { - virDomainObjLock(driver->domains.objs[i]); - if (!virDomainIsActive(driver->domains.objs[i])) - ninactive++; - virDomainObjUnlock(driver->domains.objs[i]); - } + n = virDomainObjListNumOfDomains(&driver->domains, 0); openvzDriverUnlock(driver); - return ninactive; + return n; } static virDriver openvzDriver = { diff --git a/src/qemu/qemu_driver.c b/src/qemu/qemu_driver.c index d37b184..f977247 100644 --- a/src/qemu/qemu_driver.c +++ b/src/qemu/qemu_driver.c @@ -201,9 +201,42 @@ qemudLogReadFD(virConnectPtr conn, const char* logDir, const char* name, off_t p } +struct qemuAutostartData { + struct qemud_driver *driver; + virConnectPtr conn; +}; +static void +qemuAutostartDomain(void *payload, const char *name ATTRIBUTE_UNUSED, void *opaque) +{ + virDomainObjPtr vm = payload; + struct qemuAutostartData *data = opaque; + + virDomainObjLock(vm); + if (vm->autostart && + !virDomainIsActive(vm)) { + int ret; + + virResetLastError(); + ret = qemudStartVMDaemon(data->conn, data->driver, vm, NULL, -1); + if (ret < 0) { + virErrorPtr err = virGetLastError(); + VIR_ERROR(_("Failed to autostart VM '%s': %s\n"), + vm->def->name, + err ? err->message : ""); + } else { + virDomainEventPtr event = + virDomainEventNewFromObj(vm, + VIR_DOMAIN_EVENT_STARTED, + VIR_DOMAIN_EVENT_STARTED_BOOTED); + if (event) + qemuDomainEventQueue(data->driver, event); + } + } + virDomainObjUnlock(vm); +} + static void qemudAutostartConfigs(struct qemud_driver *driver) { - unsigned int i; /* XXX: Figure out a better way todo this. The domain * startup code needs a connection handle in order * to lookup the bridge associated with a virtual @@ -213,33 +246,10 @@ qemudAutostartConfigs(struct qemud_driver *driver) { "qemu:///system" : "qemu:///session"); /* Ignoring NULL conn which is mostly harmless here */ + struct qemuAutostartData data = { driver, conn }; qemuDriverLock(driver); - for (i = 0 ; i < driver->domains.count ; i++) { - virDomainObjPtr vm = driver->domains.objs[i]; - virDomainObjLock(vm); - if (vm->autostart && - !virDomainIsActive(vm)) { - int ret; - - virResetLastError(); - ret = qemudStartVMDaemon(conn, driver, vm, NULL, -1); - if (ret < 0) { - virErrorPtr err = virGetLastError(); - VIR_ERROR(_("Failed to autostart VM '%s': %s\n"), - vm->def->name, - err ? err->message : ""); - } else { - virDomainEventPtr event = - virDomainEventNewFromObj(vm, - VIR_DOMAIN_EVENT_STARTED, - VIR_DOMAIN_EVENT_STARTED_BOOTED); - if (event) - qemuDomainEventQueue(driver, event); - } - } - virDomainObjUnlock(vm); - } + virHashForEach(driver->domains.objs, qemuAutostartDomain, &data); qemuDriverUnlock(driver); if (conn) @@ -284,7 +294,6 @@ cleanup: static int qemudOpenMonitor(virConnectPtr conn, - struct qemud_driver* driver, virDomainObjPtr vm, int reconnect); @@ -293,13 +302,16 @@ static int qemudOpenMonitor(virConnectPtr conn, * Open an existing VM's monitor, re-detect VCPU threads * and re-reserve the security labels in use */ -static int -qemuReconnectDomain(struct qemud_driver *driver, - virDomainObjPtr obj) +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, driver, obj, 1)) != 0) { + if ((rc = qemudOpenMonitor(NULL, obj, 1)) != 0) { VIR_ERROR(_("Failed to reconnect monitor for %s: %d\n"), obj->def->name, rc); goto error; @@ -313,15 +325,20 @@ qemuReconnectDomain(struct qemud_driver *driver, driver->securityDriver && driver->securityDriver->domainReserveSecurityLabel && driver->securityDriver->domainReserveSecurityLabel(NULL, obj) < 0) - return -1; + goto error; if (obj->def->id >= driver->nextvmid) driver->nextvmid = obj->def->id + 1; - return 0; + virDomainObjUnlock(obj); + return; error: - return -1; + /* We can't get the monitor back, so must kill the VM + * to remove danger of it ending up running twice if + * user tries to start it again later */ + qemudShutdownVMDaemon(NULL, driver, obj); + virDomainObjUnlock(obj); } /** @@ -333,20 +350,7 @@ error: static void qemuReconnectDomains(struct qemud_driver *driver) { - int i; - - for (i = 0 ; i < driver->domains.count ; i++) { - virDomainObjPtr obj = driver->domains.objs[i]; - - virDomainObjLock(obj); - if (qemuReconnectDomain(driver, obj) < 0) { - /* If we can't get the monitor back, then kill the VM - * so user has ability to start it again later without - * danger of ending up running twice */ - qemudShutdownVMDaemon(NULL, driver, obj); - } - virDomainObjUnlock(obj); - } + virHashForEach(driver->domains.objs, qemuReconnectDomain, driver); } @@ -438,8 +442,11 @@ qemudStartup(int privileged) { /* Don't have a dom0 so start from 1 */ qemu_driver->nextvmid = 1; + if (virDomainObjListInit(&qemu_driver->domains) < 0) + goto out_of_memory; + /* Init callback list */ - if(VIR_ALLOC(qemu_driver->domainEventCallbacks) < 0) + if (VIR_ALLOC(qemu_driver->domainEventCallbacks) < 0) goto out_of_memory; if (!(qemu_driver->domainEventQueue = virDomainEventQueueNew())) goto out_of_memory; @@ -679,21 +686,14 @@ qemudReload(void) { */ static int qemudActive(void) { - unsigned int i; int active = 0; if (!qemu_driver) return 0; + /* XXX having to iterate here is not great because it requires many locks */ qemuDriverLock(qemu_driver); - for (i = 0 ; i < qemu_driver->domains.count ; i++) { - virDomainObjPtr vm = qemu_driver->domains.objs[i]; - virDomainObjLock(vm); - if (virDomainIsActive(vm)) - active = 1; - virDomainObjUnlock(vm); - } - + active = virDomainObjListNumOfDomains(&qemu_driver->domains, 1); qemuDriverUnlock(qemu_driver); return active; } @@ -713,7 +713,7 @@ qemudShutdown(void) { pciDeviceListFree(NULL, qemu_driver->activePciHostdevs); virCapabilitiesFree(qemu_driver->caps); - virDomainObjListFree(&qemu_driver->domains); + virDomainObjListDeinit(&qemu_driver->domains); VIR_FREE(qemu_driver->securityDriverName); VIR_FREE(qemu_driver->logDir); @@ -913,7 +913,6 @@ qemudCheckMonitorPrompt(virConnectPtr conn ATTRIBUTE_UNUSED, static int qemudOpenMonitorCommon(virConnectPtr conn, - struct qemud_driver* driver, virDomainObjPtr vm, int monfd, int reconnect) @@ -952,7 +951,7 @@ qemudOpenMonitorCommon(virConnectPtr conn, if ((vm->monitorWatch = virEventAddHandle(vm->monitor, VIR_EVENT_HANDLE_HANGUP | VIR_EVENT_HANDLE_ERROR, qemudDispatchVMEvent, - driver, NULL)) < 0) + vm, NULL)) < 0) return -1; return 0; @@ -960,7 +959,6 @@ qemudOpenMonitorCommon(virConnectPtr conn, static int qemudOpenMonitorUnix(virConnectPtr conn, - struct qemud_driver* driver, virDomainObjPtr vm, const char *monitor, int reconnect) @@ -1008,7 +1006,7 @@ qemudOpenMonitorUnix(virConnectPtr conn, goto error; } - if (qemudOpenMonitorCommon(conn, driver, vm, monfd, reconnect) < 0) + if (qemudOpenMonitorCommon(conn, vm, monfd, reconnect) < 0) goto error; return 0; @@ -1020,7 +1018,6 @@ error: static int qemudOpenMonitorPty(virConnectPtr conn, - struct qemud_driver* driver, virDomainObjPtr vm, const char *monitor, int reconnect) @@ -1033,7 +1030,7 @@ qemudOpenMonitorPty(virConnectPtr conn, return -1; } - if (qemudOpenMonitorCommon(conn, driver, vm, monfd, reconnect) < 0) + if (qemudOpenMonitorCommon(conn, vm, monfd, reconnect) < 0) goto error; return 0; @@ -1045,17 +1042,16 @@ error: static int qemudOpenMonitor(virConnectPtr conn, - struct qemud_driver *driver, virDomainObjPtr vm, int reconnect) { switch (vm->monitor_chr->type) { case VIR_DOMAIN_CHR_TYPE_UNIX: - return qemudOpenMonitorUnix(conn, driver, vm, + return qemudOpenMonitorUnix(conn, vm, vm->monitor_chr->data.nix.path, reconnect); case VIR_DOMAIN_CHR_TYPE_PTY: - return qemudOpenMonitorPty(conn, driver, vm, + return qemudOpenMonitorPty(conn, vm, vm->monitor_chr->data.file.path, reconnect); default: @@ -1177,7 +1173,7 @@ qemudWaitForMonitor(virConnectPtr conn, return -1; } - if (qemudOpenMonitor(conn, driver, vm, 0) < 0) + if (qemudOpenMonitor(conn, vm, 0) < 0) return -1; return 0; @@ -2255,28 +2251,22 @@ retry: static void qemudDispatchVMEvent(int watch, int fd, int events, void *opaque) { - struct qemud_driver *driver = opaque; - virDomainObjPtr vm = NULL; + struct qemud_driver *driver = qemu_driver; + virDomainObjPtr vm = opaque; virDomainEventPtr event = NULL; - unsigned int i; 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); - for (i = 0 ; i < driver->domains.count ; i++) { - virDomainObjPtr tmpvm = driver->domains.objs[i]; - virDomainObjLock(tmpvm); - if (virDomainIsActive(tmpvm) && - tmpvm->monitorWatch == watch) { - vm = tmpvm; - break; - } - virDomainObjUnlock(tmpvm); - } - - if (!vm) - goto cleanup; + virDomainObjLock(vm); + qemuDriverUnlock(driver); - if (vm->monitor != fd) { + if (vm->monitor != fd || vm->monitorWatch != watch) { failed = 1; } else { if (events & (VIR_EVENT_HANDLE_HANGUP | VIR_EVENT_HANDLE_ERROR)) @@ -2302,12 +2292,12 @@ qemudDispatchVMEvent(int watch, int fd, int events, void *opaque) { } } -cleanup: - if (vm) - virDomainObjUnlock(vm); - if (event) + virDomainObjUnlock(vm); + if (event) { + qemuDriverLock(driver); qemuDomainEventQueue(driver, event); - qemuDriverUnlock(driver); + qemuDriverUnlock(driver); + } } @@ -2637,31 +2627,21 @@ qemudGetHostname (virConnectPtr conn) static int qemudListDomains(virConnectPtr conn, int *ids, int nids) { struct qemud_driver *driver = conn->privateData; - int got = 0, i; + int n; qemuDriverLock(driver); - for (i = 0 ; i < driver->domains.count && got < nids ; i++) { - virDomainObjLock(driver->domains.objs[i]); - if (virDomainIsActive(driver->domains.objs[i])) - ids[got++] = driver->domains.objs[i]->def->id; - virDomainObjUnlock(driver->domains.objs[i]); - } + n = virDomainObjListGetActiveIDs(&driver->domains, ids, nids); qemuDriverUnlock(driver); - return got; + return n; } static int qemudNumDomains(virConnectPtr conn) { struct qemud_driver *driver = conn->privateData; - int n = 0, i; + int n; qemuDriverLock(driver); - for (i = 0 ; i < driver->domains.count ; i++) { - virDomainObjLock(driver->domains.objs[i]); - if (virDomainIsActive(driver->domains.objs[i])) - n++; - virDomainObjUnlock(driver->domains.objs[i]); - } + n = virDomainObjListNumOfDomains(&driver->domains, 1); qemuDriverUnlock(driver); return n; @@ -4059,39 +4039,20 @@ cleanup: static int qemudListDefinedDomains(virConnectPtr conn, char **const names, int nnames) { struct qemud_driver *driver = conn->privateData; - int got = 0, i; + int n; qemuDriverLock(driver); - for (i = 0 ; i < driver->domains.count && got < nnames ; i++) { - virDomainObjLock(driver->domains.objs[i]); - if (!virDomainIsActive(driver->domains.objs[i])) { - if (!(names[got++] = strdup(driver->domains.objs[i]->def->name))) { - virReportOOMError(conn); - virDomainObjUnlock(driver->domains.objs[i]); - goto cleanup; - } - } - virDomainObjUnlock(driver->domains.objs[i]); - } - + n = virDomainObjListGetInactiveNames(&driver->domains, names, nnames); qemuDriverUnlock(driver); - return got; - - cleanup: - for (i = 0 ; i < got ; i++) - VIR_FREE(names[i]); - qemuDriverUnlock(driver); - return -1; + return n; } static int qemudNumDefinedDomains(virConnectPtr conn) { struct qemud_driver *driver = conn->privateData; - int n = 0, i; + int n; qemuDriverLock(driver); - for (i = 0 ; i < driver->domains.count ; i++) - if (!virDomainIsActive(driver->domains.objs[i])) - n++; + n = virDomainObjListNumOfDomains(&driver->domains, 0); qemuDriverUnlock(driver); return n; diff --git a/src/test/test_driver.c b/src/test/test_driver.c index 0541a73..919a2f6 100644 --- a/src/test/test_driver.c +++ b/src/test/test_driver.c @@ -338,6 +338,9 @@ static int testOpenDefault(virConnectPtr conn) { goto error; } + if (virDomainObjListInit(&privconn->domains) < 0) + goto error; + memmove(&privconn->nodeInfo, &defaultNodeInfo, sizeof(defaultNodeInfo)); // Numa setup @@ -419,7 +422,7 @@ static int testOpenDefault(virConnectPtr conn) { return VIR_DRV_OPEN_SUCCESS; error: - virDomainObjListFree(&privconn->domains); + virDomainObjListDeinit(&privconn->domains); virNetworkObjListFree(&privconn->networks); virInterfaceObjListFree(&privconn->ifaces); virStoragePoolObjListFree(&privconn->pools); @@ -569,6 +572,9 @@ static int testOpenFromFile(virConnectPtr conn, testDriverLock(privconn); conn->privateData = privconn; + if (virDomainObjListInit(&privconn->domains) < 0) + goto error; + if (!(privconn->caps = testBuildCapabilities(conn))) goto error; @@ -897,7 +903,7 @@ static int testOpenFromFile(virConnectPtr conn, VIR_FREE(pools); if (fd != -1) close(fd); - virDomainObjListFree(&privconn->domains); + virDomainObjListDeinit(&privconn->domains); virNetworkObjListFree(&privconn->networks); virInterfaceObjListFree(&privconn->ifaces); virStoragePoolObjListFree(&privconn->pools); @@ -966,7 +972,7 @@ static int testClose(virConnectPtr conn) testConnPtr privconn = conn->privateData; testDriverLock(privconn); virCapabilitiesFree(privconn->caps); - virDomainObjListFree(&privconn->domains); + virDomainObjListDeinit(&privconn->domains); virNetworkObjListFree(&privconn->networks); virInterfaceObjListFree(&privconn->ifaces); virStoragePoolObjListFree(&privconn->pools); @@ -1036,15 +1042,13 @@ static char *testGetCapabilities (virConnectPtr conn) static int testNumOfDomains(virConnectPtr conn) { testConnPtr privconn = conn->privateData; - unsigned int numActive = 0, i; + int count; testDriverLock(privconn); - for (i = 0 ; i < privconn->domains.count ; i++) - if (virDomainIsActive(privconn->domains.objs[i])) - numActive++; + count = virDomainObjListNumOfDomains(&privconn->domains, 1); testDriverUnlock(privconn); - return numActive; + return count; } static virDomainPtr @@ -1173,15 +1177,10 @@ static int testListDomains (virConnectPtr conn, int maxids) { testConnPtr privconn = conn->privateData; - unsigned int n = 0, i; + int n; testDriverLock(privconn); - for (i = 0 ; i < privconn->domains.count && n < maxids ; i++) { - virDomainObjLock(privconn->domains.objs[i]); - if (virDomainIsActive(privconn->domains.objs[i])) - ids[n++] = privconn->domains.objs[i]->def->id; - virDomainObjUnlock(privconn->domains.objs[i]); - } + n = virDomainObjListGetActiveIDs(&privconn->domains, ids, maxids); testDriverUnlock(privconn); return n; @@ -1852,47 +1851,28 @@ cleanup: static int testNumOfDefinedDomains(virConnectPtr conn) { testConnPtr privconn = conn->privateData; - unsigned int numInactive = 0, i; + int count; testDriverLock(privconn); - for (i = 0 ; i < privconn->domains.count ; i++) { - virDomainObjLock(privconn->domains.objs[i]); - if (!virDomainIsActive(privconn->domains.objs[i])) - numInactive++; - virDomainObjUnlock(privconn->domains.objs[i]); - } + count = virDomainObjListNumOfDomains(&privconn->domains, 0); testDriverUnlock(privconn); - return numInactive; + return count; } static int testListDefinedDomains(virConnectPtr conn, char **const names, int maxnames) { + testConnPtr privconn = conn->privateData; - unsigned int n = 0, i; + int n; testDriverLock(privconn); memset(names, 0, sizeof(*names)*maxnames); - for (i = 0 ; i < privconn->domains.count && n < maxnames ; i++) { - virDomainObjLock(privconn->domains.objs[i]); - if (!virDomainIsActive(privconn->domains.objs[i]) && - !(names[n++] = strdup(privconn->domains.objs[i]->def->name))) { - virDomainObjUnlock(privconn->domains.objs[i]); - goto no_memory; - } - virDomainObjUnlock(privconn->domains.objs[i]); - } + n = virDomainObjListGetInactiveNames(&privconn->domains, names, maxnames); testDriverUnlock(privconn); return n; - -no_memory: - virReportOOMError(conn); - for (n = 0 ; n < maxnames ; n++) - VIR_FREE(names[n]); - testDriverUnlock(privconn); - return -1; } static virDomainPtr testDomainDefineXML(virConnectPtr conn, diff --git a/src/uml/uml_driver.c b/src/uml/uml_driver.c index 9a7fe42..80cf477 100644 --- a/src/uml/uml_driver.c +++ b/src/uml/uml_driver.c @@ -114,10 +114,32 @@ static int umlMonitorCommand (virConnectPtr conn, static struct uml_driver *uml_driver = NULL; +struct umlAutostartData { + struct uml_driver *driver; + virConnectPtr conn; +}; + +static void +umlAutostartDomain(void *payload, const char *name ATTRIBUTE_UNUSED, void *opaque) +{ + virDomainObjPtr vm = payload; + const struct umlAutostartData *data = opaque; + + virDomainObjLock(vm); + if (vm->autostart && + !virDomainIsActive(vm)) { + virResetLastError(); + if (umlStartVMDaemon(data->conn, data->driver, vm) < 0) { + virErrorPtr err = virGetLastError(); + VIR_ERROR(_("Failed to autostart VM '%s': %s"), + vm->def->name, err->message); + } + } + virDomainObjUnlock(vm); +} static void umlAutostartConfigs(struct uml_driver *driver) { - unsigned int i; /* XXX: Figure out a better way todo this. The domain * startup code needs a connection handle in order * to lookup the bridge associated with a virtual @@ -128,15 +150,9 @@ umlAutostartConfigs(struct uml_driver *driver) { "uml:///session"); /* Ignoring NULL conn which is mostly harmless here */ - for (i = 0 ; i < driver->domains.count ; i++) { - if (driver->domains.objs[i]->autostart && - !virDomainIsActive(driver->domains.objs[i]) && - umlStartVMDaemon(conn, driver, driver->domains.objs[i]) < 0) { - virErrorPtr err = virGetLastError(); - VIR_ERROR(_("Failed to autostart VM '%s': %s"), - driver->domains.objs[i]->def->name, err->message); - } - } + struct umlAutostartData data = { driver, conn }; + + virHashForEach(driver->domains.objs, umlAutostartDomain, &data); if (conn) virConnectClose(conn); @@ -320,6 +336,9 @@ umlStartup(int privileged) { uml_driver->nextvmid = 1; uml_driver->inotifyWatch = -1; + if (virDomainObjListInit(¨_driver->domains) < 0) + goto error; + userdir = virGetUserDirectory(NULL, uid); if (!userdir) goto error; @@ -449,24 +468,30 @@ umlReload(void) { */ static int umlActive(void) { - unsigned int i; int active = 0; if (!uml_driver) return 0; umlDriverLock(uml_driver); - for (i = 0 ; i < uml_driver->domains.count ; i++) { - virDomainObjLock(uml_driver->domains.objs[i]); - if (virDomainIsActive(uml_driver->domains.objs[i])) - active = 1; - virDomainObjUnlock(uml_driver->domains.objs[i]); - } + active = virDomainObjListNumOfDomains(¨_driver->domains, 1); umlDriverUnlock(uml_driver); return active; } +static void +umlShutdownOneVM(void *payload, const char *name ATTRIBUTE_UNUSED, void *opaque) +{ + virDomainObjPtr dom = payload; + struct uml_driver *driver = opaque; + + virDomainObjLock(dom); + if (virDomainIsActive(dom)) + umlShutdownVMDaemon(NULL, driver, dom); + virDomainObjUnlock(dom); +} + /** * umlShutdown: * @@ -474,8 +499,6 @@ umlActive(void) { */ static int umlShutdown(void) { - unsigned int i; - if (!uml_driver) return -1; @@ -485,16 +508,11 @@ umlShutdown(void) { close(uml_driver->inotifyFD); virCapabilitiesFree(uml_driver->caps); - /* shutdown active VMs */ - for (i = 0 ; i < uml_driver->domains.count ; i++) { - virDomainObjPtr dom = uml_driver->domains.objs[i]; - virDomainObjLock(dom); - if (virDomainIsActive(dom)) - umlShutdownVMDaemon(NULL, uml_driver, dom); - virDomainObjUnlock(dom); - } + /* shutdown active VMs + * XXX allow them to stay around & reconnect */ + virHashForEach(uml_driver->domains.objs, umlShutdownOneVM, uml_driver); - virDomainObjListFree(¨_driver->domains); + virDomainObjListDeinit(¨_driver->domains); VIR_FREE(uml_driver->logDir); VIR_FREE(uml_driver->configDir); @@ -1145,30 +1163,20 @@ umlGetHostname (virConnectPtr conn) static int umlListDomains(virConnectPtr conn, int *ids, int nids) { struct uml_driver *driver = conn->privateData; - int got = 0, i; + int n; umlDriverLock(driver); - for (i = 0 ; i < driver->domains.count && got < nids ; i++) { - virDomainObjLock(driver->domains.objs[i]); - if (virDomainIsActive(driver->domains.objs[i])) - ids[got++] = driver->domains.objs[i]->def->id; - virDomainObjUnlock(driver->domains.objs[i]); - } + n = virDomainObjListGetActiveIDs(&driver->domains, ids, nids); umlDriverUnlock(driver); - return got; + return n; } static int umlNumDomains(virConnectPtr conn) { struct uml_driver *driver = conn->privateData; - int n = 0, i; + int n; umlDriverLock(driver); - for (i = 0 ; i < driver->domains.count ; i++) { - virDomainObjLock(driver->domains.objs[i]); - if (virDomainIsActive(driver->domains.objs[i])) - n++; - virDomainObjUnlock(driver->domains.objs[i]); - } + n = virDomainObjListNumOfDomains(&driver->domains, 1); umlDriverUnlock(driver); return n; @@ -1481,42 +1489,21 @@ cleanup: static int umlListDefinedDomains(virConnectPtr conn, char **const names, int nnames) { struct uml_driver *driver = conn->privateData; - int got = 0, i; + int n; umlDriverLock(driver); - for (i = 0 ; i < driver->domains.count && got < nnames ; i++) { - virDomainObjLock(driver->domains.objs[i]); - if (!virDomainIsActive(driver->domains.objs[i])) { - if (!(names[got++] = strdup(driver->domains.objs[i]->def->name))) { - virReportOOMError(conn); - virDomainObjUnlock(driver->domains.objs[i]); - goto cleanup; - } - } - virDomainObjUnlock(driver->domains.objs[i]); - } + n = virDomainObjListGetInactiveNames(&driver->domains, names, nnames); umlDriverUnlock(driver); - return got; - - cleanup: - for (i = 0 ; i < got ; i++) - VIR_FREE(names[i]); - umlDriverUnlock(driver); - return -1; + return n; } static int umlNumDefinedDomains(virConnectPtr conn) { struct uml_driver *driver = conn->privateData; - int n = 0, i; + int n; umlDriverLock(driver); - for (i = 0 ; i < driver->domains.count ; i++) { - virDomainObjLock(driver->domains.objs[i]); - if (!virDomainIsActive(driver->domains.objs[i])) - n++; - virDomainObjUnlock(driver->domains.objs[i]); - } + n = virDomainObjListNumOfDomains(&driver->domains, 0); umlDriverUnlock(driver); return n; diff --git a/src/vbox/vbox_tmpl.c b/src/vbox/vbox_tmpl.c index 4f43901..4741c57 100644 --- a/src/vbox/vbox_tmpl.c +++ b/src/vbox/vbox_tmpl.c @@ -103,7 +103,6 @@ typedef struct { virMutex lock; int version; - virDomainObjList domains; virCapsPtr caps; IVirtualBox *vboxObj; @@ -485,7 +484,6 @@ static void vboxUninitialize(vboxGlobalData *data) { if (data->pFuncs) data->pFuncs->pfnComUninitialize(); - virDomainObjListFree(&data->domains); virCapabilitiesFree(data->caps); #if VBOX_API_VERSION == 2002 /* No domainEventCallbacks in 2.2.* version */ -- 1.6.2.5

On Fri, Oct 23, 2009 at 02:05:30PM +0100, Daniel P. Berrange wrote:
The current virDomainObjListPtr object stores domain objects in an array. This means that to find a particular objects requires O(n) time, and more critically acquiring O(n) mutex locks.
The new impl replaces the array with a virHashTable, keyed off UUID. Finding a object based on UUID is now O(1) time, and only requires a single mutex lock. Finding by name/id is unchanged in complexity.
In changing this, all code which iterates over the array had to be updated to use a hash table iterator function callback. Several of the functions which were identically duplicating across all drivers were pulled into domain_conf.c
* src/conf/domain_conf.h, src/conf/domain_conf.c: Change virDomainObjListPtr to use virHashTable. Add a initializer method virDomainObjListInit, and rename virDomainObjListFree to virDomainObjListDeinit, since its not actually freeing the container, only its contents. Also add some convenient methods virDomainObjListGetInactiveNames, virDomainObjListGetActiveIDs and virDomainObjListNumOfDomains which can be used to implement the correspondingly named public API entry points in drivers * src/libvirt_private.syms: Export new methods from domain_conf.h * src/lxc/lxc_driver.c, src/opennebula/one_driver.c, src/openvz/openvz_conf.c, src/openvz/openvz_driver.c, src/qemu/qemu_driver.c, src/test/test_driver.c, src/uml/uml_driver.c, src/vbox/vbox_tmpl.c: Update all code to deal with hash tables instead of arrays for domains --- src/conf/domain_conf.c | 274 ++++++++++++++++++++++++++++++------------- src/conf/domain_conf.h | 19 +++- src/libvirt_private.syms | 6 +- src/lxc/lxc_driver.c | 247 +++++++++++++++++---------------------- src/opennebula/one_driver.c | 68 +++-------- src/openvz/openvz_conf.c | 7 +- src/openvz/openvz_driver.c | 25 ++--- src/qemu/qemu_driver.c | 221 ++++++++++++++-------------------- src/test/test_driver.c | 60 +++------- src/uml/uml_driver.c | 125 +++++++++----------- src/vbox/vbox_tmpl.c | 2 - 11 files changed, 519 insertions(+), 535 deletions(-)
Okay, this is a fairly intrusive patch, I looked but didn't spot anything, I guess this is best handled by testing it as much as possible. Since this is not directly related to the monitor handling change, I would suggest to push it now to benefit from amximum testing. 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/

This implements a thin wrapper around the pthread_rwlock primitives. No impl is provided for Win32 at this time since it is rather hard, and none of our code yet requires it on Win32 * src/util/threads.h: Add virRWLockInit, virRWLockDestroy, virRWLockRead, virRWLockWrite, virRWLockUnlock APIs * src/util/threads-pthread.h: define virRWLock struct * src/util/threads-pthread.c: Implement RWLock APIs --- src/libvirt_private.syms | 6 ++++++ src/util/threads-pthread.c | 30 ++++++++++++++++++++++++++++++ src/util/threads-pthread.h | 4 ++++ src/util/threads.h | 10 ++++++++++ 4 files changed, 50 insertions(+), 0 deletions(-) diff --git a/src/libvirt_private.syms b/src/libvirt_private.syms index bd9d84a..6ed562d 100644 --- a/src/libvirt_private.syms +++ b/src/libvirt_private.syms @@ -426,6 +426,12 @@ virCondWait; virCondSignal; virCondBroadcast; +virRWLockInit; +virRWLockDestroy; +virRWLockRead; +virRWLockWrite; +virRWLockUnlock; + # util.h virFileReadAll; virFileWriteStr; diff --git a/src/util/threads-pthread.c b/src/util/threads-pthread.c index 4e00bc5..2052c0a 100644 --- a/src/util/threads-pthread.c +++ b/src/util/threads-pthread.c @@ -57,6 +57,36 @@ void virMutexUnlock(virMutexPtr m) } +int virRWLockInit(virRWLockPtr m) +{ + if (pthread_rwlock_init(&m->lock, NULL) != 0) { + errno = EINVAL; + return -1; + } + return 0; +} + +void virRWLockDestroy(virRWLockPtr m) +{ + pthread_rwlock_destroy(&m->lock); +} + +void virRWLockRead(virRWLockPtr m) +{ + pthread_rwlock_rdlock(&m->lock); +} + +void virRWLockWrite(virRWLockPtr m) +{ + pthread_rwlock_wrlock(&m->lock); +} + +void virRWLockUnlock(virRWLockPtr m) +{ + pthread_rwlock_unlock(&m->lock); +} + + int virCondInit(virCondPtr c) { diff --git a/src/util/threads-pthread.h b/src/util/threads-pthread.h index 6404d1d..f2b546e 100644 --- a/src/util/threads-pthread.h +++ b/src/util/threads-pthread.h @@ -27,6 +27,10 @@ struct virMutex { pthread_mutex_t lock; }; +struct virRWLock { + pthread_rwlock_t lock; +}; + struct virCond { pthread_cond_t cond; }; diff --git a/src/util/threads.h b/src/util/threads.h index 62239b7..be65031 100644 --- a/src/util/threads.h +++ b/src/util/threads.h @@ -27,6 +27,9 @@ typedef struct virMutex virMutex; typedef virMutex *virMutexPtr; +typedef struct virRWLock virRWLock; +typedef virRWLock *virRWLockPtr; + typedef struct virCond virCond; typedef virCond *virCondPtr; @@ -44,6 +47,13 @@ void virMutexLock(virMutexPtr m); void virMutexUnlock(virMutexPtr m); +int virRWLockInit(virRWLockPtr m) ATTRIBUTE_RETURN_CHECK; +void virRWLockDestroy(virRWLockPtr m); + +void virRWLockRead(virRWLockPtr m); +void virRWLockWrite(virRWLockPtr m); +void virRWLockUnlock(virRWLockPtr m); + int virCondInit(virCondPtr c) ATTRIBUTE_RETURN_CHECK; int virCondDestroy(virCondPtr c) ATTRIBUTE_RETURN_CHECK; -- 1.6.2.5

On Fri, Oct 23, 2009 at 02:05:31PM +0100, Daniel P. Berrange wrote:
This implements a thin wrapper around the pthread_rwlock primitives. No impl is provided for Win32 at this time since it is rather hard, and none of our code yet requires it on Win32
* src/util/threads.h: Add virRWLockInit, virRWLockDestroy, virRWLockRead, virRWLockWrite, virRWLockUnlock APIs * src/util/threads-pthread.h: define virRWLock struct * src/util/threads-pthread.c: Implement RWLock APIs --- src/libvirt_private.syms | 6 ++++++ src/util/threads-pthread.c | 30 ++++++++++++++++++++++++++++++ src/util/threads-pthread.h | 4 ++++ src/util/threads.h | 10 ++++++++++ 4 files changed, 50 insertions(+), 0 deletions(-)
diff --git a/src/libvirt_private.syms b/src/libvirt_private.syms index bd9d84a..6ed562d 100644 --- a/src/libvirt_private.syms +++ b/src/libvirt_private.syms @@ -426,6 +426,12 @@ virCondWait; virCondSignal; virCondBroadcast;
+virRWLockInit; +virRWLockDestroy; +virRWLockRead; +virRWLockWrite; +virRWLockUnlock; + # util.h virFileReadAll; virFileWriteStr; diff --git a/src/util/threads-pthread.c b/src/util/threads-pthread.c index 4e00bc5..2052c0a 100644 --- a/src/util/threads-pthread.c +++ b/src/util/threads-pthread.c @@ -57,6 +57,36 @@ void virMutexUnlock(virMutexPtr m) }
+int virRWLockInit(virRWLockPtr m) +{ + if (pthread_rwlock_init(&m->lock, NULL) != 0) { + errno = EINVAL; + return -1; + } + return 0; +} + +void virRWLockDestroy(virRWLockPtr m) +{ + pthread_rwlock_destroy(&m->lock); +} + +void virRWLockRead(virRWLockPtr m) +{ + pthread_rwlock_rdlock(&m->lock); +} + +void virRWLockWrite(virRWLockPtr m) +{ + pthread_rwlock_wrlock(&m->lock); +} + +void virRWLockUnlock(virRWLockPtr m) +{ + pthread_rwlock_unlock(&m->lock); +} + +
Hum it's a small patch, but I would rather fix those function to not be void when the underlying pthread_ counterpart can actually fail. IMHO we should report errors coming from the thread library (whichever is used on a given platform). 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, Oct 28, 2009 at 05:15:05PM +0100, Daniel Veillard wrote:
On Fri, Oct 23, 2009 at 02:05:31PM +0100, Daniel P. Berrange wrote:
This implements a thin wrapper around the pthread_rwlock primitives. No impl is provided for Win32 at this time since it is rather hard, and none of our code yet requires it on Win32
* src/util/threads.h: Add virRWLockInit, virRWLockDestroy, virRWLockRead, virRWLockWrite, virRWLockUnlock APIs * src/util/threads-pthread.h: define virRWLock struct * src/util/threads-pthread.c: Implement RWLock APIs --- src/libvirt_private.syms | 6 ++++++ src/util/threads-pthread.c | 30 ++++++++++++++++++++++++++++++ src/util/threads-pthread.h | 4 ++++ src/util/threads.h | 10 ++++++++++ 4 files changed, 50 insertions(+), 0 deletions(-)
diff --git a/src/libvirt_private.syms b/src/libvirt_private.syms index bd9d84a..6ed562d 100644 --- a/src/libvirt_private.syms +++ b/src/libvirt_private.syms @@ -426,6 +426,12 @@ virCondWait; virCondSignal; virCondBroadcast;
+virRWLockInit; +virRWLockDestroy; +virRWLockRead; +virRWLockWrite; +virRWLockUnlock; + # util.h virFileReadAll; virFileWriteStr; diff --git a/src/util/threads-pthread.c b/src/util/threads-pthread.c index 4e00bc5..2052c0a 100644 --- a/src/util/threads-pthread.c +++ b/src/util/threads-pthread.c @@ -57,6 +57,36 @@ void virMutexUnlock(virMutexPtr m) }
+int virRWLockInit(virRWLockPtr m) +{ + if (pthread_rwlock_init(&m->lock, NULL) != 0) { + errno = EINVAL; + return -1; + } + return 0; +} + +void virRWLockDestroy(virRWLockPtr m) +{ + pthread_rwlock_destroy(&m->lock); +} + +void virRWLockRead(virRWLockPtr m) +{ + pthread_rwlock_rdlock(&m->lock); +} + +void virRWLockWrite(virRWLockPtr m) +{ + pthread_rwlock_wrlock(&m->lock); +} + +void virRWLockUnlock(virRWLockPtr m) +{ + pthread_rwlock_unlock(&m->lock); +} + +
Hum it's a small patch, but I would rather fix those function to not be void when the underlying pthread_ counterpart can actually fail. IMHO we should report errors coming from the thread library (whichever is used on a given platform).
I mis-understood the manpage for this - I read it to mean these calls would actually deadlock upon error (as a mutex would), but in fact they return error EDEADLOCK instead which is rather unhelpful. I'm going to withdraw this entire 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 :|

A number of driver API methods which acquire the driver mutex only ever used the driver object in a read-only fashion. All these uses are converted to call qemuDriverLockRO() allowing for greater concurrency. * src/qemu/qemu_conf.h: s/Mutex/RWLock/ * src/qemu/qemu_driver.c: Add a qemuDriverLockRO() method and use it anywhere that doesn't require a write lock on the driver --- src/qemu/qemu_conf.h | 2 +- src/qemu/qemu_driver.c | 100 +++++++++++++++++++++++++++-------------------- 2 files changed, 58 insertions(+), 44 deletions(-) diff --git a/src/qemu/qemu_conf.h b/src/qemu/qemu_conf.h index f9a970f..a6e68f8 100644 --- a/src/qemu/qemu_conf.h +++ b/src/qemu/qemu_conf.h @@ -75,7 +75,7 @@ enum qemud_cmd_flags { /* Main driver state */ struct qemud_driver { - virMutex lock; + virRWLock lock; int privileged; diff --git a/src/qemu/qemu_driver.c b/src/qemu/qemu_driver.c index f977247..fc6cc53 100644 --- a/src/qemu/qemu_driver.c +++ b/src/qemu/qemu_driver.c @@ -78,13 +78,19 @@ static int qemudShutdown(void); +/* Obtains a full write lock */ static void qemuDriverLock(struct qemud_driver *driver) { - virMutexLock(&driver->lock); + virRWLockWrite(&driver->lock); +} +/* Obtains a read-only lock */ +static void qemuDriverLockRO(struct qemud_driver *driver) +{ + virRWLockRead(&driver->lock); } static void qemuDriverUnlock(struct qemud_driver *driver) { - virMutexUnlock(&driver->lock); + virRWLockUnlock(&driver->lock); } static void qemuDomainEventFlush(int timer, void *opaque); @@ -431,8 +437,8 @@ qemudStartup(int privileged) { if (VIR_ALLOC(qemu_driver) < 0) return -1; - if (virMutexInit(&qemu_driver->lock) < 0) { - VIR_ERROR("%s", _("cannot initialize mutex")); + if (virRWLockInit(&qemu_driver->lock) < 0) { + VIR_ERROR("%s", _("cannot initialize rwlock")); VIR_FREE(qemu_driver); return -1; } @@ -743,7 +749,7 @@ qemudShutdown(void) { virCgroupFree(&qemu_driver->cgroup); qemuDriverUnlock(qemu_driver); - virMutexDestroy(&qemu_driver->lock); + virRWLockDestroy(&qemu_driver->lock); VIR_FREE(qemu_driver); return 0; @@ -2262,7 +2268,7 @@ qemudDispatchVMEvent(int watch, int fd, int events, void *opaque) { * 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); + qemuDriverLockRO(driver); virDomainObjLock(vm); qemuDriverUnlock(driver); @@ -2523,7 +2529,7 @@ static virDomainPtr qemudDomainLookupByID(virConnectPtr conn, virDomainObjPtr vm; virDomainPtr dom = NULL; - qemuDriverLock(driver); + qemuDriverLockRO(driver); vm = virDomainFindByID(&driver->domains, id); qemuDriverUnlock(driver); @@ -2548,7 +2554,7 @@ static virDomainPtr qemudDomainLookupByUUID(virConnectPtr conn, virDomainObjPtr vm; virDomainPtr dom = NULL; - qemuDriverLock(driver); + qemuDriverLockRO(driver); vm = virDomainFindByUUID(&driver->domains, uuid); qemuDriverUnlock(driver); @@ -2575,7 +2581,7 @@ static virDomainPtr qemudDomainLookupByName(virConnectPtr conn, virDomainObjPtr vm; virDomainPtr dom = NULL; - qemuDriverLock(driver); + qemuDriverLockRO(driver); vm = virDomainFindByName(&driver->domains, name); qemuDriverUnlock(driver); @@ -2629,7 +2635,7 @@ static int qemudListDomains(virConnectPtr conn, int *ids, int nids) { struct qemud_driver *driver = conn->privateData; int n; - qemuDriverLock(driver); + qemuDriverLockRO(driver); n = virDomainObjListGetActiveIDs(&driver->domains, ids, nids); qemuDriverUnlock(driver); @@ -2640,7 +2646,7 @@ static int qemudNumDomains(virConnectPtr conn) { struct qemud_driver *driver = conn->privateData; int n; - qemuDriverLock(driver); + qemuDriverLockRO(driver); n = virDomainObjListNumOfDomains(&driver->domains, 1); qemuDriverUnlock(driver); @@ -2734,7 +2740,7 @@ static int qemudDomainSuspend(virDomainPtr dom) { int ret = -1; virDomainEventPtr event = NULL; - qemuDriverLock(driver); + qemuDriverLockRO(driver); vm = virDomainFindByUUID(&driver->domains, dom->uuid); if (!vm) { @@ -2764,7 +2770,9 @@ static int qemudDomainSuspend(virDomainPtr dom) { cleanup: if (vm) virDomainObjUnlock(vm); + qemuDriverUnlock(driver); + qemuDriverLock(driver); if (event) qemuDomainEventQueue(driver, event); qemuDriverUnlock(driver); @@ -2778,7 +2786,7 @@ static int qemudDomainResume(virDomainPtr dom) { int ret = -1; virDomainEventPtr event = NULL; - qemuDriverLock(driver); + qemuDriverLockRO(driver); vm = virDomainFindByUUID(&driver->domains, dom->uuid); if (!vm) { @@ -2812,6 +2820,8 @@ static int qemudDomainResume(virDomainPtr dom) { cleanup: if (vm) virDomainObjUnlock(vm); + qemuDriverUnlock(driver); + qemuDriverLock(driver); if (event) qemuDomainEventQueue(driver, event); qemuDriverUnlock(driver); @@ -2824,7 +2834,7 @@ static int qemudDomainShutdown(virDomainPtr dom) { virDomainObjPtr vm; int ret = -1; - qemuDriverLock(driver); + qemuDriverLockRO(driver); vm = virDomainFindByUUID(&driver->domains, dom->uuid); qemuDriverUnlock(driver); @@ -2901,7 +2911,7 @@ static char *qemudDomainGetOSType(virDomainPtr dom) { virDomainObjPtr vm; char *type = NULL; - qemuDriverLock(driver); + qemuDriverLockRO(driver); vm = virDomainFindByUUID(&driver->domains, dom->uuid); qemuDriverUnlock(driver); if (!vm) { @@ -2927,7 +2937,7 @@ static unsigned long qemudDomainGetMaxMemory(virDomainPtr dom) { virDomainObjPtr vm; unsigned long ret = 0; - qemuDriverLock(driver); + qemuDriverLockRO(driver); vm = virDomainFindByUUID(&driver->domains, dom->uuid); qemuDriverUnlock(driver); @@ -2952,7 +2962,7 @@ static int qemudDomainSetMaxMemory(virDomainPtr dom, unsigned long newmax) { virDomainObjPtr vm; int ret = -1; - qemuDriverLock(driver); + qemuDriverLockRO(driver); vm = virDomainFindByUUID(&driver->domains, dom->uuid); qemuDriverUnlock(driver); @@ -2985,7 +2995,7 @@ static int qemudDomainSetMemory(virDomainPtr dom, unsigned long newmem) { virDomainObjPtr vm; int ret = -1; - qemuDriverLock(driver); + qemuDriverLockRO(driver); vm = virDomainFindByUUID(&driver->domains, dom->uuid); qemuDriverUnlock(driver); if (!vm) { @@ -3032,7 +3042,7 @@ static int qemudDomainGetInfo(virDomainPtr dom, int err; unsigned long balloon; - qemuDriverLock(driver); + qemuDriverLockRO(driver); vm = virDomainFindByUUID(&driver->domains, dom->uuid); qemuDriverUnlock(driver); if (!vm) { @@ -3260,7 +3270,7 @@ static int qemudDomainCoreDump(virDomainPtr dom, NULL, }; - qemuDriverLock(driver); + qemuDriverLockRO(driver); vm = virDomainFindByUUID(&driver->domains, dom->uuid); qemuDriverUnlock(driver); @@ -3317,7 +3327,7 @@ static int qemudDomainSetVcpus(virDomainPtr dom, unsigned int nvcpus) { int ret = -1; const char *type; - qemuDriverLock(driver); + qemuDriverLockRO(driver); vm = virDomainFindByUUID(&driver->domains, dom->uuid); qemuDriverUnlock(driver); @@ -3378,7 +3388,7 @@ qemudDomainPinVcpu(virDomainPtr dom, virNodeInfo nodeinfo; int ret = -1; - qemuDriverLock(driver); + qemuDriverLockRO(driver); vm = virDomainFindByUUID(&driver->domains, dom->uuid); qemuDriverUnlock(driver); @@ -3447,7 +3457,7 @@ qemudDomainGetVcpus(virDomainPtr dom, int i, v, maxcpu; int ret = -1; - qemuDriverLock(driver); + qemuDriverLockRO(driver); vm = virDomainFindByUUID(&driver->domains, dom->uuid); qemuDriverUnlock(driver); @@ -3536,7 +3546,7 @@ static int qemudDomainGetMaxVcpus(virDomainPtr dom) { const char *type; int ret = -1; - qemuDriverLock(driver); + qemuDriverLockRO(driver); vm = virDomainFindByUUID(&driver->domains, dom->uuid); qemuDriverUnlock(driver); @@ -3570,7 +3580,7 @@ static int qemudDomainGetSecurityLabel(virDomainPtr dom, virSecurityLabelPtr sec const char *type; int ret = -1; - qemuDriverLock(driver); + qemuDriverLockRO(driver); vm = virDomainFindByUUID(&driver->domains, dom->uuid); memset(seclabel, 0, sizeof(*seclabel)); @@ -3630,7 +3640,7 @@ static int qemudNodeGetSecurityModel(virConnectPtr conn, char *p; int ret = 0; - qemuDriverLock(driver); + qemuDriverLockRO(driver); if (!driver->securityDriver) { memset(secmodel, 0, sizeof (*secmodel)); goto cleanup; @@ -3846,7 +3856,7 @@ static char *qemudDomainDumpXML(virDomainPtr dom, unsigned long balloon; int err; - qemuDriverLock(driver); + qemuDriverLockRO(driver); vm = virDomainFindByUUID(&driver->domains, dom->uuid); qemuDriverUnlock(driver); @@ -3894,7 +3904,7 @@ static char *qemuDomainXMLFromNative(virConnectPtr conn, goto cleanup; } - qemuDriverLock(driver); + qemuDriverLockRO(driver); def = qemuParseCommandLineString(conn, driver->caps, config); qemuDriverUnlock(driver); if (!def) @@ -3924,7 +3934,7 @@ static char *qemuDomainXMLToNative(virConnectPtr conn, char *ret = NULL; int i; - qemuDriverLock(driver); + qemuDriverLockRO(driver); if (STRNEQ(format, QEMU_CONFIG_FORMAT_ARGV)) { qemudReportError(conn, NULL, NULL, VIR_ERR_INVALID_ARG, @@ -4041,7 +4051,7 @@ static int qemudListDefinedDomains(virConnectPtr conn, struct qemud_driver *driver = conn->privateData; int n; - qemuDriverLock(driver); + qemuDriverLockRO(driver); n = virDomainObjListGetInactiveNames(&driver->domains, names, nnames); qemuDriverUnlock(driver); return n; @@ -4051,7 +4061,7 @@ static int qemudNumDefinedDomains(virConnectPtr conn) { struct qemud_driver *driver = conn->privateData; int n; - qemuDriverLock(driver); + qemuDriverLockRO(driver); n = virDomainObjListNumOfDomains(&driver->domains, 0); qemuDriverUnlock(driver); @@ -4065,7 +4075,7 @@ static int qemudDomainStart(virDomainPtr dom) { int ret = -1; virDomainEventPtr event = NULL; - qemuDriverLock(driver); + qemuDriverLockRO(driver); vm = virDomainFindByUUID(&driver->domains, dom->uuid); if (!vm) { @@ -4085,6 +4095,8 @@ static int qemudDomainStart(virDomainPtr dom) { cleanup: if (vm) virDomainObjUnlock(vm); + qemuDriverUnlock(driver); + qemuDriverLock(driver); if (event) qemuDomainEventQueue(driver, event); qemuDriverUnlock(driver); @@ -5154,7 +5166,7 @@ static int qemudDomainGetAutostart(virDomainPtr dom, virDomainObjPtr vm; int ret = -1; - qemuDriverLock(driver); + qemuDriverLockRO(driver); vm = virDomainFindByUUID(&driver->domains, dom->uuid); qemuDriverUnlock(driver); @@ -5182,7 +5194,7 @@ static int qemudDomainSetAutostart(virDomainPtr dom, char *configFile = NULL, *autostartLink = NULL; int ret = -1; - qemuDriverLock(driver); + qemuDriverLockRO(driver); vm = virDomainFindByUUID(&driver->domains, dom->uuid); if (!vm) { @@ -5252,7 +5264,7 @@ static char *qemuGetSchedulerType(virDomainPtr dom, struct qemud_driver *driver = dom->conn->privateData; char *ret = NULL; - qemuDriverLock(driver); + qemuDriverLockRO(driver); if (!qemuCgroupControllerActive(driver, VIR_CGROUP_CONTROLLER_CPU)) { qemudReportError(dom->conn, dom, NULL, VIR_ERR_NO_SUPPORT, __FUNCTION__); @@ -5281,7 +5293,7 @@ static int qemuSetSchedulerParameters(virDomainPtr dom, virDomainObjPtr vm = NULL; int ret = -1; - qemuDriverLock(driver); + qemuDriverLockRO(driver); if (!qemuCgroupControllerActive(driver, VIR_CGROUP_CONTROLLER_CPU)) { qemudReportError(dom->conn, dom, NULL, VIR_ERR_NO_SUPPORT, __FUNCTION__); @@ -5346,7 +5358,7 @@ static int qemuGetSchedulerParameters(virDomainPtr dom, int ret = -1; int rc; - qemuDriverLock(driver); + qemuDriverLockRO(driver); if (!qemuCgroupControllerActive(driver, VIR_CGROUP_CONTROLLER_CPU)) { qemudReportError(dom->conn, dom, NULL, VIR_ERR_NO_SUPPORT, __FUNCTION__); @@ -5413,7 +5425,7 @@ qemudDomainBlockStats (virDomainPtr dom, virDomainObjPtr vm; virDomainDiskDefPtr disk = NULL; - qemuDriverLock(driver); + qemuDriverLockRO(driver); vm = virDomainFindByUUID(&driver->domains, dom->uuid); qemuDriverUnlock(driver); if (!vm) { @@ -5474,7 +5486,7 @@ qemudDomainInterfaceStats (virDomainPtr dom, int i; int ret = -1; - qemuDriverLock(driver); + qemuDriverLockRO(driver); vm = virDomainFindByUUID(&driver->domains, dom->uuid); qemuDriverUnlock(driver); @@ -5534,7 +5546,7 @@ qemudDomainBlockPeek (virDomainPtr dom, virDomainObjPtr vm; int fd = -1, ret = -1, i; - qemuDriverLock(driver); + qemuDriverLockRO(driver); vm = virDomainFindByUUID(&driver->domains, dom->uuid); qemuDriverUnlock(driver); @@ -5607,7 +5619,7 @@ qemudDomainMemoryPeek (virDomainPtr dom, char *tmp = NULL; int fd = -1, ret = -1; - qemuDriverLock(driver); + qemuDriverLockRO(driver); vm = virDomainFindByUUID(&driver->domains, dom->uuid); qemuDriverUnlock(driver); @@ -6762,7 +6774,7 @@ qemudDomainMigratePerform (virDomainPtr dom, int ret = -1; int paused = 0; - qemuDriverLock(driver); + qemuDriverLockRO(driver); vm = virDomainFindByUUID(&driver->domains, dom->uuid); if (!vm) { char uuidstr[VIR_UUID_STRING_BUFLEN]; @@ -6834,6 +6846,8 @@ cleanup: if (vm) virDomainObjUnlock(vm); + qemuDriverUnlock(driver); + qemuDriverLock(driver); if (event) qemuDomainEventQueue(driver, event); qemuDriverUnlock(driver); @@ -7041,7 +7055,7 @@ qemudNodeDeviceReset (virNodeDevicePtr dev) if (!pci) return -1; - qemuDriverLock(driver); + qemuDriverLockRO(driver); if (pciResetDevice(dev->conn, pci, driver->activePciHostdevs) < 0) goto out; -- 1.6.2.5

On Fri, Oct 23, 2009 at 02:05:32PM +0100, Daniel P. Berrange wrote:
A number of driver API methods which acquire the driver mutex only ever used the driver object in a read-only fashion. All these uses are converted to call qemuDriverLockRO() allowing for greater concurrency.
* src/qemu/qemu_conf.h: s/Mutex/RWLock/ * src/qemu/qemu_driver.c: Add a qemuDriverLockRO() method and use it anywhere that doesn't require a write lock on the driver
Hum, I still wonder about erro handling strategies there, for example even taking a read lock may fail not because of a programing error because there is already too many read lock taken. [...]
@@ -6834,6 +6846,8 @@ cleanup:
if (vm) virDomainObjUnlock(vm); + qemuDriverUnlock(driver); + qemuDriverLock(driver); if (event) qemuDomainEventQueue(driver, event); qemuDriverUnlock(driver);
Huh ??? really ? we need a way to allow other threads to get in ? Maybe a tiny wait here would allow a rescheduling especially if on a single processor. But otherwise this looks like a fairly automatic conversion so should go in with 02/21 when ready, 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, Oct 28, 2009 at 05:24:00PM +0100, Daniel Veillard wrote:
On Fri, Oct 23, 2009 at 02:05:32PM +0100, Daniel P. Berrange wrote:
A number of driver API methods which acquire the driver mutex only ever used the driver object in a read-only fashion. All these uses are converted to call qemuDriverLockRO() allowing for greater concurrency.
* src/qemu/qemu_conf.h: s/Mutex/RWLock/ * src/qemu/qemu_driver.c: Add a qemuDriverLockRO() method and use it anywhere that doesn't require a write lock on the driver
Hum, I still wonder about erro handling strategies there, for example even taking a read lock may fail not because of a programing error because there is already too many read lock taken.
[...]
@@ -6834,6 +6846,8 @@ cleanup:
if (vm) virDomainObjUnlock(vm); + qemuDriverUnlock(driver); + qemuDriverLock(driver); if (event) qemuDomainEventQueue(driver, event); qemuDriverUnlock(driver);
Huh ??? really ? we need a way to allow other threads to get in ? Maybe a tiny wait here would allow a rescheduling especially if on a single processor.
This is a merge error.
But otherwise this looks like a fairly automatic conversion so should go in with 02/21 when ready, ACK
I'm withdrawing this patch, and the one implementing RWLock. I believe I have a more effective way to deal with concurrency. See the mail I just sent out.... 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 :|

The virDomainObjPtr object stores state about a running domain. This object is shared across all drivers so it is not appropriate to include driver specific state here. This patch adds the ability to request a blob of private data per domain object instance. The driver must provide a allocator & deallocator for this purpose THis patch abuses the virCapabilitiesPtr structure for storing the allocator/deallocator callbacks, since it is already being abused for other internal things relating to parsing. This should be moved out into a separate object at some point. * src/conf/capabilities.h: Add privateDataAllocFunc and privateDataFreeFunc fields * src/conf/domain_conf.c: Invoke the driver allocators / deallocators when creating/freeing virDomainObjPtr instances. * src/conf/domain_conf.h: Pass virCapsPtr into virDomainAssignDef to allow access to the driver specific allocator function * src/lxc/lxc_driver.c, src/opennebula/one_driver.c, src/openvz/openvz_driver.c, src/qemu/qemu_driver.c, src/test/test_driver.c, src/uml/uml_driver.c: Update for change in virDomainAssignDef contract --- src/conf/capabilities.h | 2 ++ src/conf/domain_conf.c | 23 +++++++++++++++++++---- src/conf/domain_conf.h | 4 ++++ src/lxc/lxc_driver.c | 6 ++++-- src/opennebula/one_driver.c | 6 ++++-- src/openvz/openvz_driver.c | 6 ++++-- src/qemu/qemu_driver.c | 5 +++++ src/test/test_driver.c | 15 ++++++++++----- src/uml/uml_driver.c | 2 ++ 9 files changed, 54 insertions(+), 15 deletions(-) diff --git a/src/conf/capabilities.h b/src/conf/capabilities.h index 2f24605..7234cf4 100644 --- a/src/conf/capabilities.h +++ b/src/conf/capabilities.h @@ -115,6 +115,8 @@ struct _virCaps { virCapsGuestPtr *guests; unsigned char macPrefix[VIR_MAC_PREFIX_BUFLEN]; unsigned int emulatorRequired : 1; + void *(*privateDataAllocFunc)(void); + void (*privateDataFreeFunc)(void *); }; diff --git a/src/conf/domain_conf.c b/src/conf/domain_conf.c index de07e13..0dd2b3f 100644 --- a/src/conf/domain_conf.c +++ b/src/conf/domain_conf.c @@ -594,12 +594,16 @@ void virDomainObjFree(virDomainObjPtr dom) VIR_FREE(dom->vcpupids); + if (dom->privateDataFreeFunc) + (dom->privateDataFreeFunc)(dom->privateData); + virMutexDestroy(&dom->lock); VIR_FREE(dom); } -static virDomainObjPtr virDomainObjNew(virConnectPtr conn) +static virDomainObjPtr virDomainObjNew(virConnectPtr conn, + virCapsPtr caps) { virDomainObjPtr domain; @@ -608,9 +612,19 @@ static virDomainObjPtr virDomainObjNew(virConnectPtr conn) return NULL; } + if (caps->privateDataAllocFunc && + !(domain->privateData = (caps->privateDataAllocFunc)())) { + virReportOOMError(conn); + VIR_FREE(domain); + return NULL; + } + domain->privateDataFreeFunc = caps->privateDataFreeFunc; + if (virMutexInit(&domain->lock) < 0) { virDomainReportError(conn, VIR_ERR_INTERNAL_ERROR, "%s", _("cannot initialize mutex")); + if (domain->privateDataFreeFunc) + (domain->privateDataFreeFunc)(domain->privateData); VIR_FREE(domain); return NULL; } @@ -624,6 +638,7 @@ static virDomainObjPtr virDomainObjNew(virConnectPtr conn) } virDomainObjPtr virDomainAssignDef(virConnectPtr conn, + virCapsPtr caps, virDomainObjListPtr doms, const virDomainDefPtr def) { @@ -643,7 +658,7 @@ virDomainObjPtr virDomainAssignDef(virConnectPtr conn, return domain; } - if (!(domain = virDomainObjNew(conn))) + if (!(domain = virDomainObjNew(conn, caps))) return NULL; domain->def = def; @@ -3187,7 +3202,7 @@ static virDomainObjPtr virDomainObjParseXML(virConnectPtr conn, xmlNodePtr *nodes = NULL; int n, i; - if (!(obj = virDomainObjNew(conn))) + if (!(obj = virDomainObjNew(conn, caps))) return NULL; if (!(config = virXPathNode(conn, "./domain", ctxt))) { @@ -4768,7 +4783,7 @@ virDomainObjPtr virDomainLoadConfig(virConnectPtr conn, newVM = 0; } - if (!(dom = virDomainAssignDef(conn, doms, def))) + if (!(dom = virDomainAssignDef(conn, caps, doms, def))) goto error; dom->autostart = autostart; diff --git a/src/conf/domain_conf.h b/src/conf/domain_conf.h index 389e259..8a8bfb0 100644 --- a/src/conf/domain_conf.h +++ b/src/conf/domain_conf.h @@ -636,6 +636,9 @@ struct _virDomainObj { virDomainDefPtr def; /* The current definition */ virDomainDefPtr newDef; /* New definition to activate at shutdown */ + + void *privateData; + void (*privateDataFreeFunc)(void *); }; typedef struct _virDomainObjList virDomainObjList; @@ -678,6 +681,7 @@ void virDomainDefFree(virDomainDefPtr vm); void virDomainObjFree(virDomainObjPtr vm); virDomainObjPtr virDomainAssignDef(virConnectPtr conn, + virCapsPtr caps, virDomainObjListPtr doms, const virDomainDefPtr def); void virDomainRemoveInactive(virDomainObjListPtr doms, diff --git a/src/lxc/lxc_driver.c b/src/lxc/lxc_driver.c index 4f0787b..116f3ae 100644 --- a/src/lxc/lxc_driver.c +++ b/src/lxc/lxc_driver.c @@ -315,7 +315,8 @@ static virDomainPtr lxcDomainDefine(virConnectPtr conn, const char *xml) goto cleanup; } - if (!(vm = virDomainAssignDef(conn, &driver->domains, def))) + if (!(vm = virDomainAssignDef(conn, driver->caps, + &driver->domains, def))) goto cleanup; def = NULL; vm->persistent = 1; @@ -1312,7 +1313,8 @@ lxcDomainCreateAndStart(virConnectPtr conn, } - if (!(vm = virDomainAssignDef(conn, &driver->domains, def))) + if (!(vm = virDomainAssignDef(conn, driver->caps, + &driver->domains, def))) goto cleanup; def = NULL; diff --git a/src/opennebula/one_driver.c b/src/opennebula/one_driver.c index 0b807ad..beb48ce 100644 --- a/src/opennebula/one_driver.c +++ b/src/opennebula/one_driver.c @@ -235,7 +235,8 @@ static virDomainPtr oneDomainDefine(virConnectPtr conn, const char *xml) VIR_DOMAIN_XML_INACTIVE))) goto return_point; - if (!(vm = virDomainAssignDef(conn, &driver->domains, def))) { + if (!(vm = virDomainAssignDef(conn, driver->caps, + &driver->domains, def))) { virDomainDefFree(def); goto return_point; } @@ -439,7 +440,8 @@ oneDomainCreateAndStart(virConnectPtr conn, goto return_point; } - if (!(vm = virDomainAssignDef(conn, &driver->domains, def))) { + if (!(vm = virDomainAssignDef(conn, driver->caps, + &driver->domains, def))) { virDomainDefFree(def); goto return_point; } diff --git a/src/openvz/openvz_driver.c b/src/openvz/openvz_driver.c index b0092cd..d334235 100644 --- a/src/openvz/openvz_driver.c +++ b/src/openvz/openvz_driver.c @@ -774,7 +774,8 @@ openvzDomainDefineXML(virConnectPtr conn, const char *xml) vmdef->name); goto cleanup; } - if (!(vm = virDomainAssignDef(conn, &driver->domains, vmdef))) + if (!(vm = virDomainAssignDef(conn, driver->caps, + &driver->domains, vmdef))) goto cleanup; vmdef = NULL; @@ -841,7 +842,8 @@ openvzDomainCreateXML(virConnectPtr conn, const char *xml, vmdef->name); goto cleanup; } - if (!(vm = virDomainAssignDef(conn, &driver->domains, vmdef))) + if (!(vm = virDomainAssignDef(conn, driver->caps, + &driver->domains, vmdef))) goto cleanup; vmdef = NULL; diff --git a/src/qemu/qemu_driver.c b/src/qemu/qemu_driver.c index fc6cc53..1651071 100644 --- a/src/qemu/qemu_driver.c +++ b/src/qemu/qemu_driver.c @@ -2703,6 +2703,7 @@ static virDomainPtr qemudDomainCreate(virConnectPtr conn, const char *xml, } if (!(vm = virDomainAssignDef(conn, + driver->caps, &driver->domains, def))) goto cleanup; @@ -3766,6 +3767,7 @@ static int qemudDomainRestore(virConnectPtr conn, } if (!(vm = virDomainAssignDef(conn, + driver->caps, &driver->domains, def))) { qemudReportError(conn, NULL, NULL, VIR_ERR_OPERATION_FAILED, @@ -4254,6 +4256,7 @@ static virDomainPtr qemudDomainDefine(virConnectPtr conn, const char *xml) { goto cleanup; if (!(vm = virDomainAssignDef(conn, + driver->caps, &driver->domains, def))) { goto cleanup; @@ -6112,6 +6115,7 @@ qemudDomainMigratePrepareTunnel(virConnectPtr dconn, } if (!(vm = virDomainAssignDef(dconn, + driver->caps, &driver->domains, def))) { qemudReportError(dconn, NULL, NULL, VIR_ERR_OPERATION_FAILED, @@ -6333,6 +6337,7 @@ qemudDomainMigratePrepare2 (virConnectPtr dconn, } if (!(vm = virDomainAssignDef(dconn, + driver->caps, &driver->domains, def))) { qemudReportError (dconn, NULL, NULL, VIR_ERR_OPERATION_FAILED, diff --git a/src/test/test_driver.c b/src/test/test_driver.c index 919a2f6..f08fe74 100644 --- a/src/test/test_driver.c +++ b/src/test/test_driver.c @@ -364,7 +364,8 @@ static int testOpenDefault(virConnectPtr conn) { goto error; if (testDomainGenerateIfnames(conn, domdef) < 0) goto error; - if (!(domobj = virDomainAssignDef(conn, &privconn->domains, domdef))) + if (!(domobj = virDomainAssignDef(conn, privconn->caps, + &privconn->domains, domdef))) goto error; domdef = NULL; domobj->def->id = privconn->nextDomID++; @@ -716,7 +717,8 @@ static int testOpenFromFile(virConnectPtr conn, } if (testDomainGenerateIfnames(conn, def) < 0 || - !(dom = virDomainAssignDef(conn, &privconn->domains, def))) { + !(dom = virDomainAssignDef(conn, privconn->caps, + &privconn->domains, def))) { virDomainDefFree(def); goto error; } @@ -1068,7 +1070,8 @@ testDomainCreateXML(virConnectPtr conn, const char *xml, if (testDomainGenerateIfnames(conn, def) < 0) goto cleanup; - if (!(dom = virDomainAssignDef(conn, &privconn->domains, def))) + if (!(dom = virDomainAssignDef(conn, privconn->caps, + &privconn->domains, def))) goto cleanup; def = NULL; dom->state = VIR_DOMAIN_RUNNING; @@ -1616,7 +1619,8 @@ static int testDomainRestore(virConnectPtr conn, if (testDomainGenerateIfnames(conn, def) < 0) goto cleanup; - if (!(dom = virDomainAssignDef(conn, &privconn->domains, def))) + if (!(dom = virDomainAssignDef(conn, privconn->caps, + &privconn->domains, def))) goto cleanup; def = NULL; @@ -1890,7 +1894,8 @@ static virDomainPtr testDomainDefineXML(virConnectPtr conn, if (testDomainGenerateIfnames(conn, def) < 0) goto cleanup; - if (!(dom = virDomainAssignDef(conn, &privconn->domains, def))) + if (!(dom = virDomainAssignDef(conn, privconn->caps, + &privconn->domains, def))) goto cleanup; def = NULL; dom->persistent = 1; diff --git a/src/uml/uml_driver.c b/src/uml/uml_driver.c index 80cf477..4fb04d9 100644 --- a/src/uml/uml_driver.c +++ b/src/uml/uml_driver.c @@ -1212,6 +1212,7 @@ static virDomainPtr umlDomainCreate(virConnectPtr conn, const char *xml, } if (!(vm = virDomainAssignDef(conn, + driver->caps, &driver->domains, def))) goto cleanup; @@ -1546,6 +1547,7 @@ static virDomainPtr umlDomainDefine(virConnectPtr conn, const char *xml) { goto cleanup; if (!(vm = virDomainAssignDef(conn, + driver->caps, &driver->domains, def))) goto cleanup; -- 1.6.2.5

On Fri, Oct 23, 2009 at 02:05:33PM +0100, Daniel P. Berrange wrote:
The virDomainObjPtr object stores state about a running domain. This object is shared across all drivers so it is not appropriate to include driver specific state here. This patch adds the ability to request a blob of private data per domain object instance. The driver must provide a allocator & deallocator for this purpose
THis patch abuses the virCapabilitiesPtr structure for storing the allocator/deallocator callbacks, since it is already being abused for other internal things relating to parsing. This should be moved out into a separate object at some point.
* src/conf/capabilities.h: Add privateDataAllocFunc and privateDataFreeFunc fields * src/conf/domain_conf.c: Invoke the driver allocators / deallocators when creating/freeing virDomainObjPtr instances. * src/conf/domain_conf.h: Pass virCapsPtr into virDomainAssignDef to allow access to the driver specific allocator function * src/lxc/lxc_driver.c, src/opennebula/one_driver.c, src/openvz/openvz_driver.c, src/qemu/qemu_driver.c, src/test/test_driver.c, src/uml/uml_driver.c: Update for change in virDomainAssignDef contract --- src/conf/capabilities.h | 2 ++ src/conf/domain_conf.c | 23 +++++++++++++++++++---- src/conf/domain_conf.h | 4 ++++ src/lxc/lxc_driver.c | 6 ++++-- src/opennebula/one_driver.c | 6 ++++-- src/openvz/openvz_driver.c | 6 ++++-- src/qemu/qemu_driver.c | 5 +++++ src/test/test_driver.c | 15 ++++++++++----- src/uml/uml_driver.c | 2 ++ 9 files changed, 54 insertions(+), 15 deletions(-)
diff --git a/src/conf/capabilities.h b/src/conf/capabilities.h index 2f24605..7234cf4 100644 --- a/src/conf/capabilities.h +++ b/src/conf/capabilities.h @@ -115,6 +115,8 @@ struct _virCaps { virCapsGuestPtr *guests; unsigned char macPrefix[VIR_MAC_PREFIX_BUFLEN]; unsigned int emulatorRequired : 1; + void *(*privateDataAllocFunc)(void); + void (*privateDataFreeFunc)(void *); };
diff --git a/src/conf/domain_conf.c b/src/conf/domain_conf.c index de07e13..0dd2b3f 100644 --- a/src/conf/domain_conf.c +++ b/src/conf/domain_conf.c @@ -594,12 +594,16 @@ void virDomainObjFree(virDomainObjPtr dom)
VIR_FREE(dom->vcpupids);
+ if (dom->privateDataFreeFunc) + (dom->privateDataFreeFunc)(dom->privateData); + virMutexDestroy(&dom->lock);
VIR_FREE(dom); }
-static virDomainObjPtr virDomainObjNew(virConnectPtr conn) +static virDomainObjPtr virDomainObjNew(virConnectPtr conn, + virCapsPtr caps) { virDomainObjPtr domain;
@@ -608,9 +612,19 @@ static virDomainObjPtr virDomainObjNew(virConnectPtr conn) return NULL; }
+ if (caps->privateDataAllocFunc && + !(domain->privateData = (caps->privateDataAllocFunc)())) { + virReportOOMError(conn); + VIR_FREE(domain); + return NULL; + } + domain->privateDataFreeFunc = caps->privateDataFreeFunc; + if (virMutexInit(&domain->lock) < 0) { virDomainReportError(conn, VIR_ERR_INTERNAL_ERROR, "%s", _("cannot initialize mutex")); + if (domain->privateDataFreeFunc) + (domain->privateDataFreeFunc)(domain->privateData); VIR_FREE(domain); return NULL; } @@ -624,6 +638,7 @@ static virDomainObjPtr virDomainObjNew(virConnectPtr conn) }
virDomainObjPtr virDomainAssignDef(virConnectPtr conn, + virCapsPtr caps, virDomainObjListPtr doms, const virDomainDefPtr def) { @@ -643,7 +658,7 @@ virDomainObjPtr virDomainAssignDef(virConnectPtr conn, return domain; }
- if (!(domain = virDomainObjNew(conn))) + if (!(domain = virDomainObjNew(conn, caps))) return NULL; domain->def = def;
@@ -3187,7 +3202,7 @@ static virDomainObjPtr virDomainObjParseXML(virConnectPtr conn, xmlNodePtr *nodes = NULL; int n, i;
- if (!(obj = virDomainObjNew(conn))) + if (!(obj = virDomainObjNew(conn, caps))) return NULL;
if (!(config = virXPathNode(conn, "./domain", ctxt))) { @@ -4768,7 +4783,7 @@ virDomainObjPtr virDomainLoadConfig(virConnectPtr conn, newVM = 0; }
- if (!(dom = virDomainAssignDef(conn, doms, def))) + if (!(dom = virDomainAssignDef(conn, caps, doms, def))) goto error;
dom->autostart = autostart; diff --git a/src/conf/domain_conf.h b/src/conf/domain_conf.h index 389e259..8a8bfb0 100644 --- a/src/conf/domain_conf.h +++ b/src/conf/domain_conf.h @@ -636,6 +636,9 @@ struct _virDomainObj {
virDomainDefPtr def; /* The current definition */ virDomainDefPtr newDef; /* New definition to activate at shutdown */ + + void *privateData; + void (*privateDataFreeFunc)(void *); };
typedef struct _virDomainObjList virDomainObjList; @@ -678,6 +681,7 @@ void virDomainDefFree(virDomainDefPtr vm); void virDomainObjFree(virDomainObjPtr vm);
virDomainObjPtr virDomainAssignDef(virConnectPtr conn, + virCapsPtr caps, virDomainObjListPtr doms, const virDomainDefPtr def); void virDomainRemoveInactive(virDomainObjListPtr doms, diff --git a/src/lxc/lxc_driver.c b/src/lxc/lxc_driver.c index 4f0787b..116f3ae 100644 --- a/src/lxc/lxc_driver.c +++ b/src/lxc/lxc_driver.c @@ -315,7 +315,8 @@ static virDomainPtr lxcDomainDefine(virConnectPtr conn, const char *xml) goto cleanup; }
- if (!(vm = virDomainAssignDef(conn, &driver->domains, def))) + if (!(vm = virDomainAssignDef(conn, driver->caps, + &driver->domains, def))) goto cleanup; def = NULL; vm->persistent = 1; @@ -1312,7 +1313,8 @@ lxcDomainCreateAndStart(virConnectPtr conn, }
- if (!(vm = virDomainAssignDef(conn, &driver->domains, def))) + if (!(vm = virDomainAssignDef(conn, driver->caps, + &driver->domains, def))) goto cleanup; def = NULL;
diff --git a/src/opennebula/one_driver.c b/src/opennebula/one_driver.c index 0b807ad..beb48ce 100644 --- a/src/opennebula/one_driver.c +++ b/src/opennebula/one_driver.c @@ -235,7 +235,8 @@ static virDomainPtr oneDomainDefine(virConnectPtr conn, const char *xml) VIR_DOMAIN_XML_INACTIVE))) goto return_point;
- if (!(vm = virDomainAssignDef(conn, &driver->domains, def))) { + if (!(vm = virDomainAssignDef(conn, driver->caps, + &driver->domains, def))) { virDomainDefFree(def); goto return_point; } @@ -439,7 +440,8 @@ oneDomainCreateAndStart(virConnectPtr conn, goto return_point; }
- if (!(vm = virDomainAssignDef(conn, &driver->domains, def))) { + if (!(vm = virDomainAssignDef(conn, driver->caps, + &driver->domains, def))) { virDomainDefFree(def); goto return_point; } diff --git a/src/openvz/openvz_driver.c b/src/openvz/openvz_driver.c index b0092cd..d334235 100644 --- a/src/openvz/openvz_driver.c +++ b/src/openvz/openvz_driver.c @@ -774,7 +774,8 @@ openvzDomainDefineXML(virConnectPtr conn, const char *xml) vmdef->name); goto cleanup; } - if (!(vm = virDomainAssignDef(conn, &driver->domains, vmdef))) + if (!(vm = virDomainAssignDef(conn, driver->caps, + &driver->domains, vmdef))) goto cleanup; vmdef = NULL;
@@ -841,7 +842,8 @@ openvzDomainCreateXML(virConnectPtr conn, const char *xml, vmdef->name); goto cleanup; } - if (!(vm = virDomainAssignDef(conn, &driver->domains, vmdef))) + if (!(vm = virDomainAssignDef(conn, driver->caps, + &driver->domains, vmdef))) goto cleanup; vmdef = NULL;
diff --git a/src/qemu/qemu_driver.c b/src/qemu/qemu_driver.c index fc6cc53..1651071 100644 --- a/src/qemu/qemu_driver.c +++ b/src/qemu/qemu_driver.c @@ -2703,6 +2703,7 @@ static virDomainPtr qemudDomainCreate(virConnectPtr conn, const char *xml, }
if (!(vm = virDomainAssignDef(conn, + driver->caps, &driver->domains, def))) goto cleanup; @@ -3766,6 +3767,7 @@ static int qemudDomainRestore(virConnectPtr conn, }
if (!(vm = virDomainAssignDef(conn, + driver->caps, &driver->domains, def))) { qemudReportError(conn, NULL, NULL, VIR_ERR_OPERATION_FAILED, @@ -4254,6 +4256,7 @@ static virDomainPtr qemudDomainDefine(virConnectPtr conn, const char *xml) { goto cleanup;
if (!(vm = virDomainAssignDef(conn, + driver->caps, &driver->domains, def))) { goto cleanup; @@ -6112,6 +6115,7 @@ qemudDomainMigratePrepareTunnel(virConnectPtr dconn, }
if (!(vm = virDomainAssignDef(dconn, + driver->caps, &driver->domains, def))) { qemudReportError(dconn, NULL, NULL, VIR_ERR_OPERATION_FAILED, @@ -6333,6 +6337,7 @@ qemudDomainMigratePrepare2 (virConnectPtr dconn, }
if (!(vm = virDomainAssignDef(dconn, + driver->caps, &driver->domains, def))) { qemudReportError (dconn, NULL, NULL, VIR_ERR_OPERATION_FAILED, diff --git a/src/test/test_driver.c b/src/test/test_driver.c index 919a2f6..f08fe74 100644 --- a/src/test/test_driver.c +++ b/src/test/test_driver.c @@ -364,7 +364,8 @@ static int testOpenDefault(virConnectPtr conn) { goto error; if (testDomainGenerateIfnames(conn, domdef) < 0) goto error; - if (!(domobj = virDomainAssignDef(conn, &privconn->domains, domdef))) + if (!(domobj = virDomainAssignDef(conn, privconn->caps, + &privconn->domains, domdef))) goto error; domdef = NULL; domobj->def->id = privconn->nextDomID++; @@ -716,7 +717,8 @@ static int testOpenFromFile(virConnectPtr conn, }
if (testDomainGenerateIfnames(conn, def) < 0 || - !(dom = virDomainAssignDef(conn, &privconn->domains, def))) { + !(dom = virDomainAssignDef(conn, privconn->caps, + &privconn->domains, def))) { virDomainDefFree(def); goto error; } @@ -1068,7 +1070,8 @@ testDomainCreateXML(virConnectPtr conn, const char *xml,
if (testDomainGenerateIfnames(conn, def) < 0) goto cleanup; - if (!(dom = virDomainAssignDef(conn, &privconn->domains, def))) + if (!(dom = virDomainAssignDef(conn, privconn->caps, + &privconn->domains, def))) goto cleanup; def = NULL; dom->state = VIR_DOMAIN_RUNNING; @@ -1616,7 +1619,8 @@ static int testDomainRestore(virConnectPtr conn,
if (testDomainGenerateIfnames(conn, def) < 0) goto cleanup; - if (!(dom = virDomainAssignDef(conn, &privconn->domains, def))) + if (!(dom = virDomainAssignDef(conn, privconn->caps, + &privconn->domains, def))) goto cleanup; def = NULL;
@@ -1890,7 +1894,8 @@ static virDomainPtr testDomainDefineXML(virConnectPtr conn,
if (testDomainGenerateIfnames(conn, def) < 0) goto cleanup; - if (!(dom = virDomainAssignDef(conn, &privconn->domains, def))) + if (!(dom = virDomainAssignDef(conn, privconn->caps, + &privconn->domains, def))) goto cleanup; def = NULL; dom->persistent = 1; diff --git a/src/uml/uml_driver.c b/src/uml/uml_driver.c index 80cf477..4fb04d9 100644 --- a/src/uml/uml_driver.c +++ b/src/uml/uml_driver.c @@ -1212,6 +1212,7 @@ static virDomainPtr umlDomainCreate(virConnectPtr conn, const char *xml, }
if (!(vm = virDomainAssignDef(conn, + driver->caps, &driver->domains, def))) goto cleanup; @@ -1546,6 +1547,7 @@ static virDomainPtr umlDomainDefine(virConnectPtr conn, const char *xml) { goto cleanup;
if (!(vm = virDomainAssignDef(conn, + driver->caps, &driver->domains, def))) goto cleanup;
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/

Nearly all of the methods in src/util/util.h have error codes that must be checked by the caller to correct detect & report failure. Add ATTRIBUTE_RETURN_CHECK to ensure compile time validation of this * daemon/libvirtd.c: Add explicit check on return value of virAsprintf * src/conf/domain_conf.c: Add missing check on virParseMacAddr return value status & report error * src/network/bridge_driver.c: Add missing OOM check on virAsprintf and report error * src/qemu/qemu_conf.c: Add missing check on virParseMacAddr return value status & report error * src/security/security_selinux.c: Remove call to virRandomInitialize that's done in libvirt.c already * src/storage/storage_backend_logical.c: Add check & log on virRun return status * src/util/util.c: Add missing checks on virAsprintf/Run status * src/util/util.h: Annotate all methods with ATTRIBUTE_RETURN_CHECK if they return an error status code * src/vbox/vbox_tmpl.c: Add missing check on virParseMacAddr * src/xen/xm_internal.c: Add missing checks on virAsprintf * tests/qemuargv2xmltest.c: Remove bogus call to virRandomInitialize() --- daemon/libvirtd.c | 13 ++++++--- src/conf/domain_conf.c | 7 ++++- src/network/bridge_driver.c | 6 +++- src/qemu/qemu_conf.c | 9 ++++++- src/security/security_selinux.c | 2 - src/storage/storage_backend_logical.c | 5 +++- src/util/util.c | 6 +++- src/util/util.h | 44 ++++++++++++++++---------------- src/vbox/vbox_tmpl.c | 4 ++- src/xen/xm_internal.c | 6 +++- tests/qemuargv2xmltest.c | 2 - 11 files changed, 64 insertions(+), 40 deletions(-) diff --git a/daemon/libvirtd.c b/daemon/libvirtd.c index 78dfb2d..fa473ce 100644 --- a/daemon/libvirtd.c +++ b/daemon/libvirtd.c @@ -752,13 +752,16 @@ static int qemudInitPaths(struct qemud_server *server, goto snprintf_error; } - if (server->privileged) - server->logDir = strdup (LOCAL_STATE_DIR "/log/libvirt"); - else - virAsprintf(&server->logDir, "%s/.libvirt/log", dir_prefix); + if (server->privileged) { + if (!(server->logDir = strdup (LOCAL_STATE_DIR "/log/libvirt"))) + virReportOOMError(NULL); + } else { + if (virAsprintf(&server->logDir, "%s/.libvirt/log", dir_prefix) < 0) + virReportOOMError(NULL); + } if (server->logDir == NULL) - virReportOOMError(NULL); + goto cleanup; ret = 0; diff --git a/src/conf/domain_conf.c b/src/conf/domain_conf.c index 0dd2b3f..a6d8e07 100644 --- a/src/conf/domain_conf.c +++ b/src/conf/domain_conf.c @@ -1109,7 +1109,12 @@ virDomainNetDefParseXML(virConnectPtr conn, } if (macaddr) { - virParseMacAddr((const char *)macaddr, def->mac); + if (virParseMacAddr((const char *)macaddr, def->mac) < 0) { + virDomainReportError(conn, VIR_ERR_INTERNAL_ERROR, + _("unable to parse mac address '%s'"), + (const char *)macaddr); + goto error; + } } else { virCapabilitiesGenerateMac(caps, def->mac); } diff --git a/src/network/bridge_driver.c b/src/network/bridge_driver.c index 95bc810..2a6a7ab 100644 --- a/src/network/bridge_driver.c +++ b/src/network/bridge_driver.c @@ -149,7 +149,10 @@ networkFindActiveConfigs(struct network_driver *driver) { #ifdef __linux__ char *pidpath; - virAsprintf(&pidpath, "/proc/%d/exe", obj->dnsmasqPid); + if (virAsprintf(&pidpath, "/proc/%d/exe", obj->dnsmasqPid) < 0) { + virReportOOMError(NULL); + goto cleanup; + } if (virFileLinkPointsTo(pidpath, DNSMASQ) == 0) obj->dnsmasqPid = -1; VIR_FREE(pidpath); @@ -157,6 +160,7 @@ networkFindActiveConfigs(struct network_driver *driver) { } } + cleanup: virNetworkObjUnlock(obj); } } diff --git a/src/qemu/qemu_conf.c b/src/qemu/qemu_conf.c index 158e9a3..951a6c6 100644 --- a/src/qemu/qemu_conf.c +++ b/src/qemu/qemu_conf.c @@ -2841,7 +2841,14 @@ qemuParseCommandLineNet(virConnectPtr conn, for (i = 0 ; i < nkeywords ; i++) { if (STREQ(keywords[i], "macaddr")) { genmac = 0; - virParseMacAddr(values[i], def->mac); + if (virParseMacAddr(values[i], def->mac) < 0) { + qemudReportError(conn, NULL, NULL, VIR_ERR_INTERNAL_ERROR, + _("unable to parse mac address '%s'"), + values[i]); + virDomainNetDefFree(def); + def = NULL; + goto cleanup; + } } else if (STREQ(keywords[i], "model")) { def->model = values[i]; values[i] = NULL; diff --git a/src/security/security_selinux.c b/src/security/security_selinux.c index 7e0f71a..6a03af7 100644 --- a/src/security/security_selinux.c +++ b/src/security/security_selinux.c @@ -108,8 +108,6 @@ SELinuxInitialize(virConnectPtr conn) char *ptr = NULL; int fd = 0; - virRandomInitialize(time(NULL) ^ getpid()); - fd = open(selinux_virtual_domain_context_path(), O_RDONLY); if (fd < 0) { virReportSystemError(conn, errno, diff --git a/src/storage/storage_backend_logical.c b/src/storage/storage_backend_logical.c index 4389120..eac3917 100644 --- a/src/storage/storage_backend_logical.c +++ b/src/storage/storage_backend_logical.c @@ -36,6 +36,7 @@ #include "storage_conf.h" #include "util.h" #include "memory.h" +#include "logging.h" #define VIR_FROM_THIS VIR_FROM_STORAGE @@ -341,7 +342,9 @@ virStorageBackendLogicalFindPoolSources(virConnectPtr conn, * that might be hanging around, so if this fails for some reason, the * worst that happens is that scanning doesn't pick everything up */ - virRun(conn, scanprog, &exitstatus); + if (virRun(conn, scanprog, &exitstatus) < 0) { + VIR_WARN0("Failure when running vgscan to refresh physical volumes"); + } memset(&sourceList, 0, sizeof(sourceList)); sourceList.type = VIR_STORAGE_POOL_LOGICAL; diff --git a/src/util/util.c b/src/util/util.c index 98f8a14..08070da 100644 --- a/src/util/util.c +++ b/src/util/util.c @@ -1257,7 +1257,8 @@ int virFileOpenTtyAt(const char *ptmx ATTRIBUTE_UNUSED, char* virFilePid(const char *dir, const char* name) { char *pidfile; - virAsprintf(&pidfile, "%s/%s.pid", dir, name); + if (virAsprintf(&pidfile, "%s/%s.pid", dir, name) < 0) + return NULL; return pidfile; } @@ -2108,7 +2109,8 @@ void virFileWaitForDevices(virConnectPtr conn) * If this fails for any reason, we still have the backup of polling for * 5 seconds for device nodes. */ - virRun(conn, settleprog, &exitstatus); + if (virRun(conn, settleprog, &exitstatus) < 0) + {} } #else void virFileWaitForDevices(virConnectPtr conn ATTRIBUTE_UNUSED) {} diff --git a/src/util/util.h b/src/util/util.h index 8679636..3ef26e6 100644 --- a/src/util/util.h +++ b/src/util/util.h @@ -41,8 +41,8 @@ enum { VIR_EXEC_CLEAR_CAPS = (1 << 2), }; -int virSetNonBlock(int fd); -int virSetCloseExec(int fd); +int virSetNonBlock(int fd) ATTRIBUTE_RETURN_CHECK; +int virSetCloseExec(int fd) ATTRIBUTE_RETURN_CHECK; /* This will execute in the context of the first child * after fork() but before execve() */ @@ -57,7 +57,7 @@ int virExecDaemonize(virConnectPtr conn, int flags, virExecHook hook, void *data, - char *pidfile); + char *pidfile) ATTRIBUTE_RETURN_CHECK; int virExecWithHook(virConnectPtr conn, const char *const*argv, const char *const*envp, @@ -69,7 +69,7 @@ int virExecWithHook(virConnectPtr conn, int flags, virExecHook hook, void *data, - char *pidfile); + char *pidfile) ATTRIBUTE_RETURN_CHECK; int virExec(virConnectPtr conn, const char *const*argv, const char *const*envp, @@ -78,14 +78,14 @@ int virExec(virConnectPtr conn, int infd, int *outfd, int *errfd, - int flags); -int virRun(virConnectPtr conn, const char *const*argv, int *status); + int flags) ATTRIBUTE_RETURN_CHECK; +int virRun(virConnectPtr conn, const char *const*argv, int *status) ATTRIBUTE_RETURN_CHECK; -int virFileReadLimFD(int fd, int maxlen, char **buf); +int virFileReadLimFD(int fd, int maxlen, char **buf) ATTRIBUTE_RETURN_CHECK; -int virFileReadAll(const char *path, int maxlen, char **buf); +int virFileReadAll(const char *path, int maxlen, char **buf) ATTRIBUTE_RETURN_CHECK; -int virFileWriteStr(const char *path, const char *str); +int virFileWriteStr(const char *path, const char *str) ATTRIBUTE_RETURN_CHECK; int virFileMatchesNameSuffix(const char *file, const char *name, @@ -95,28 +95,28 @@ int virFileHasSuffix(const char *str, const char *suffix); int virFileStripSuffix(char *str, - const char *suffix); + const char *suffix) ATTRIBUTE_RETURN_CHECK; int virFileLinkPointsTo(const char *checkLink, const char *checkDest); int virFileResolveLink(const char *linkpath, - char **resultpath); + char **resultpath) ATTRIBUTE_RETURN_CHECK; char *virFindFileInPath(const char *file); int virFileExists(const char *path); -int virFileMakePath(const char *path); +int virFileMakePath(const char *path) ATTRIBUTE_RETURN_CHECK; int virFileBuildPath(const char *dir, const char *name, const char *ext, char *buf, - unsigned int buflen); + unsigned int buflen) ATTRIBUTE_RETURN_CHECK; int virFileAbsPath(const char *path, - char **abspath); + char **abspath) ATTRIBUTE_RETURN_CHECK; int virFileOpenTty(int *ttymaster, char **ttyName, @@ -129,13 +129,13 @@ int virFileOpenTtyAt(const char *ptmx, char* virFilePid(const char *dir, const char *name); int virFileWritePidPath(const char *path, - pid_t pid); + pid_t pid) ATTRIBUTE_RETURN_CHECK; int virFileWritePid(const char *dir, const char *name, - pid_t pid); + pid_t pid) ATTRIBUTE_RETURN_CHECK; int virFileReadPid(const char *dir, const char *name, - pid_t *pid); + pid_t *pid) ATTRIBUTE_RETURN_CHECK; int virFileDeletePid(const char *dir, const char *name); @@ -167,7 +167,7 @@ int virMacAddrCompare (const char *mac1, const char *mac2); void virSkipSpaces(const char **str); int virParseNumber(const char **str); int virAsprintf(char **strp, const char *fmt, ...) - ATTRIBUTE_FMT_PRINTF(2, 3); + ATTRIBUTE_FMT_PRINTF(2, 3) ATTRIBUTE_RETURN_CHECK; char *virStrncpy(char *dest, const char *src, size_t n, size_t destbytes) ATTRIBUTE_RETURN_CHECK; char *virStrcpy(char *dest, const char *src, size_t destbytes) @@ -179,7 +179,7 @@ char *virStrcpy(char *dest, const char *src, size_t destbytes) #define VIR_MAC_STRING_BUFLEN VIR_MAC_BUFLEN * 3 int virParseMacAddr(const char* str, - unsigned char *addr); + unsigned char *addr) ATTRIBUTE_RETURN_CHECK; void virFormatMacAddr(const unsigned char *addr, char *str); void virGenerateMacAddr(const unsigned char *prefix, @@ -233,13 +233,13 @@ char *virGetUserName(virConnectPtr conn, uid_t uid); int virGetUserID(virConnectPtr conn, const char *name, - uid_t *uid); + uid_t *uid) ATTRIBUTE_RETURN_CHECK; int virGetGroupID(virConnectPtr conn, const char *name, - gid_t *gid); + gid_t *gid) ATTRIBUTE_RETURN_CHECK; #endif -int virRandomInitialize(unsigned int seed); +int virRandomInitialize(unsigned int seed) ATTRIBUTE_RETURN_CHECK; int virRandom(int max); #ifdef HAVE_MNTENT_H diff --git a/src/vbox/vbox_tmpl.c b/src/vbox/vbox_tmpl.c index 4741c57..aecda23 100644 --- a/src/vbox/vbox_tmpl.c +++ b/src/vbox/vbox_tmpl.c @@ -2219,7 +2219,9 @@ static char *vboxDomainDumpXML(virDomainPtr dom, int flags) { MACAddress[4], MACAddress[5], MACAddress[6], MACAddress[7], MACAddress[8], MACAddress[9], MACAddress[10], MACAddress[11]); - virParseMacAddr(macaddr, def->nets[netAdpIncCnt]->mac); + /* XXX some real error handling here some day ... */ + if (virParseMacAddr(macaddr, def->nets[netAdpIncCnt]->mac) < 0) + {} netAdpIncCnt++; diff --git a/src/xen/xm_internal.c b/src/xen/xm_internal.c index 732b2d3..b52f66e 100644 --- a/src/xen/xm_internal.c +++ b/src/xen/xm_internal.c @@ -3035,14 +3035,16 @@ xenXMDomainBlockPeek (virDomainPtr dom, static char *xenXMAutostartLinkName(virDomainPtr dom) { char *ret; - virAsprintf(&ret, "/etc/xen/auto/%s", dom->name); + if (virAsprintf(&ret, "/etc/xen/auto/%s", dom->name) < 0) + return NULL; return ret; } static char *xenXMDomainConfigName(virDomainPtr dom) { char *ret; - virAsprintf(&ret, "/etc/xen/%s", dom->name); + if (virAsprintf(&ret, "/etc/xen/%s", dom->name) < 0) + return NULL; return ret; } diff --git a/tests/qemuargv2xmltest.c b/tests/qemuargv2xmltest.c index 1b16aa9..5602df0 100644 --- a/tests/qemuargv2xmltest.c +++ b/tests/qemuargv2xmltest.c @@ -109,8 +109,6 @@ mymain(int argc, char **argv) if (!abs_srcdir) abs_srcdir = getcwd(cwd, sizeof(cwd)); - virRandomInitialize(0); - if ((driver.caps = testQemuCapsInit()) == NULL) return EXIT_FAILURE; if((driver.stateDir = strdup("/nowhere")) == NULL) -- 1.6.2.5

On Fri, Oct 23, 2009 at 02:05:34PM +0100, Daniel P. Berrange wrote:
Nearly all of the methods in src/util/util.h have error codes that must be checked by the caller to correct detect & report failure. Add ATTRIBUTE_RETURN_CHECK to ensure compile time validation of this
* daemon/libvirtd.c: Add explicit check on return value of virAsprintf * src/conf/domain_conf.c: Add missing check on virParseMacAddr return value status & report error * src/network/bridge_driver.c: Add missing OOM check on virAsprintf and report error * src/qemu/qemu_conf.c: Add missing check on virParseMacAddr return value status & report error * src/security/security_selinux.c: Remove call to virRandomInitialize that's done in libvirt.c already * src/storage/storage_backend_logical.c: Add check & log on virRun return status * src/util/util.c: Add missing checks on virAsprintf/Run status * src/util/util.h: Annotate all methods with ATTRIBUTE_RETURN_CHECK if they return an error status code * src/vbox/vbox_tmpl.c: Add missing check on virParseMacAddr * src/xen/xm_internal.c: Add missing checks on virAsprintf * tests/qemuargv2xmltest.c: Remove bogus call to virRandomInitialize() --- daemon/libvirtd.c | 13 ++++++--- src/conf/domain_conf.c | 7 ++++- src/network/bridge_driver.c | 6 +++- src/qemu/qemu_conf.c | 9 ++++++- src/security/security_selinux.c | 2 - src/storage/storage_backend_logical.c | 5 +++- src/util/util.c | 6 +++- src/util/util.h | 44 ++++++++++++++++---------------- src/vbox/vbox_tmpl.c | 4 ++- src/xen/xm_internal.c | 6 +++- tests/qemuargv2xmltest.c | 2 - 11 files changed, 64 insertions(+), 40 deletions(-)
diff --git a/daemon/libvirtd.c b/daemon/libvirtd.c index 78dfb2d..fa473ce 100644 --- a/daemon/libvirtd.c +++ b/daemon/libvirtd.c @@ -752,13 +752,16 @@ static int qemudInitPaths(struct qemud_server *server, goto snprintf_error; }
- if (server->privileged) - server->logDir = strdup (LOCAL_STATE_DIR "/log/libvirt"); - else - virAsprintf(&server->logDir, "%s/.libvirt/log", dir_prefix); + if (server->privileged) { + if (!(server->logDir = strdup (LOCAL_STATE_DIR "/log/libvirt"))) + virReportOOMError(NULL); + } else { + if (virAsprintf(&server->logDir, "%s/.libvirt/log", dir_prefix) < 0) + virReportOOMError(NULL); + }
if (server->logDir == NULL) - virReportOOMError(NULL); + goto cleanup;
ret = 0;
diff --git a/src/conf/domain_conf.c b/src/conf/domain_conf.c index 0dd2b3f..a6d8e07 100644 --- a/src/conf/domain_conf.c +++ b/src/conf/domain_conf.c @@ -1109,7 +1109,12 @@ virDomainNetDefParseXML(virConnectPtr conn, }
if (macaddr) { - virParseMacAddr((const char *)macaddr, def->mac); + if (virParseMacAddr((const char *)macaddr, def->mac) < 0) { + virDomainReportError(conn, VIR_ERR_INTERNAL_ERROR, + _("unable to parse mac address '%s'"), + (const char *)macaddr); + goto error; + } } else { virCapabilitiesGenerateMac(caps, def->mac); } diff --git a/src/network/bridge_driver.c b/src/network/bridge_driver.c index 95bc810..2a6a7ab 100644 --- a/src/network/bridge_driver.c +++ b/src/network/bridge_driver.c @@ -149,7 +149,10 @@ networkFindActiveConfigs(struct network_driver *driver) { #ifdef __linux__ char *pidpath;
- virAsprintf(&pidpath, "/proc/%d/exe", obj->dnsmasqPid); + if (virAsprintf(&pidpath, "/proc/%d/exe", obj->dnsmasqPid) < 0) { + virReportOOMError(NULL); + goto cleanup; + } if (virFileLinkPointsTo(pidpath, DNSMASQ) == 0) obj->dnsmasqPid = -1; VIR_FREE(pidpath); @@ -157,6 +160,7 @@ networkFindActiveConfigs(struct network_driver *driver) { } }
+ cleanup: virNetworkObjUnlock(obj); } } diff --git a/src/qemu/qemu_conf.c b/src/qemu/qemu_conf.c index 158e9a3..951a6c6 100644 --- a/src/qemu/qemu_conf.c +++ b/src/qemu/qemu_conf.c @@ -2841,7 +2841,14 @@ qemuParseCommandLineNet(virConnectPtr conn, for (i = 0 ; i < nkeywords ; i++) { if (STREQ(keywords[i], "macaddr")) { genmac = 0; - virParseMacAddr(values[i], def->mac); + if (virParseMacAddr(values[i], def->mac) < 0) { + qemudReportError(conn, NULL, NULL, VIR_ERR_INTERNAL_ERROR, + _("unable to parse mac address '%s'"), + values[i]); + virDomainNetDefFree(def); + def = NULL; + goto cleanup; + } } else if (STREQ(keywords[i], "model")) { def->model = values[i]; values[i] = NULL; diff --git a/src/security/security_selinux.c b/src/security/security_selinux.c index 7e0f71a..6a03af7 100644 --- a/src/security/security_selinux.c +++ b/src/security/security_selinux.c @@ -108,8 +108,6 @@ SELinuxInitialize(virConnectPtr conn) char *ptr = NULL; int fd = 0;
- virRandomInitialize(time(NULL) ^ getpid()); - fd = open(selinux_virtual_domain_context_path(), O_RDONLY); if (fd < 0) { virReportSystemError(conn, errno, diff --git a/src/storage/storage_backend_logical.c b/src/storage/storage_backend_logical.c index 4389120..eac3917 100644 --- a/src/storage/storage_backend_logical.c +++ b/src/storage/storage_backend_logical.c @@ -36,6 +36,7 @@ #include "storage_conf.h" #include "util.h" #include "memory.h" +#include "logging.h"
#define VIR_FROM_THIS VIR_FROM_STORAGE
@@ -341,7 +342,9 @@ virStorageBackendLogicalFindPoolSources(virConnectPtr conn, * that might be hanging around, so if this fails for some reason, the * worst that happens is that scanning doesn't pick everything up */ - virRun(conn, scanprog, &exitstatus); + if (virRun(conn, scanprog, &exitstatus) < 0) { + VIR_WARN0("Failure when running vgscan to refresh physical volumes"); + }
memset(&sourceList, 0, sizeof(sourceList)); sourceList.type = VIR_STORAGE_POOL_LOGICAL; diff --git a/src/util/util.c b/src/util/util.c index 98f8a14..08070da 100644 --- a/src/util/util.c +++ b/src/util/util.c @@ -1257,7 +1257,8 @@ int virFileOpenTtyAt(const char *ptmx ATTRIBUTE_UNUSED, char* virFilePid(const char *dir, const char* name) { char *pidfile; - virAsprintf(&pidfile, "%s/%s.pid", dir, name); + if (virAsprintf(&pidfile, "%s/%s.pid", dir, name) < 0) + return NULL; return pidfile; }
@@ -2108,7 +2109,8 @@ void virFileWaitForDevices(virConnectPtr conn) * If this fails for any reason, we still have the backup of polling for * 5 seconds for device nodes. */ - virRun(conn, settleprog, &exitstatus); + if (virRun(conn, settleprog, &exitstatus) < 0) + {} } #else void virFileWaitForDevices(virConnectPtr conn ATTRIBUTE_UNUSED) {} diff --git a/src/util/util.h b/src/util/util.h index 8679636..3ef26e6 100644 --- a/src/util/util.h +++ b/src/util/util.h @@ -41,8 +41,8 @@ enum { VIR_EXEC_CLEAR_CAPS = (1 << 2), };
-int virSetNonBlock(int fd); -int virSetCloseExec(int fd); +int virSetNonBlock(int fd) ATTRIBUTE_RETURN_CHECK; +int virSetCloseExec(int fd) ATTRIBUTE_RETURN_CHECK;
/* This will execute in the context of the first child * after fork() but before execve() */ @@ -57,7 +57,7 @@ int virExecDaemonize(virConnectPtr conn, int flags, virExecHook hook, void *data, - char *pidfile); + char *pidfile) ATTRIBUTE_RETURN_CHECK; int virExecWithHook(virConnectPtr conn, const char *const*argv, const char *const*envp, @@ -69,7 +69,7 @@ int virExecWithHook(virConnectPtr conn, int flags, virExecHook hook, void *data, - char *pidfile); + char *pidfile) ATTRIBUTE_RETURN_CHECK; int virExec(virConnectPtr conn, const char *const*argv, const char *const*envp, @@ -78,14 +78,14 @@ int virExec(virConnectPtr conn, int infd, int *outfd, int *errfd, - int flags); -int virRun(virConnectPtr conn, const char *const*argv, int *status); + int flags) ATTRIBUTE_RETURN_CHECK; +int virRun(virConnectPtr conn, const char *const*argv, int *status) ATTRIBUTE_RETURN_CHECK;
-int virFileReadLimFD(int fd, int maxlen, char **buf); +int virFileReadLimFD(int fd, int maxlen, char **buf) ATTRIBUTE_RETURN_CHECK;
-int virFileReadAll(const char *path, int maxlen, char **buf); +int virFileReadAll(const char *path, int maxlen, char **buf) ATTRIBUTE_RETURN_CHECK;
-int virFileWriteStr(const char *path, const char *str); +int virFileWriteStr(const char *path, const char *str) ATTRIBUTE_RETURN_CHECK;
int virFileMatchesNameSuffix(const char *file, const char *name, @@ -95,28 +95,28 @@ int virFileHasSuffix(const char *str, const char *suffix);
int virFileStripSuffix(char *str, - const char *suffix); + const char *suffix) ATTRIBUTE_RETURN_CHECK;
int virFileLinkPointsTo(const char *checkLink, const char *checkDest);
int virFileResolveLink(const char *linkpath, - char **resultpath); + char **resultpath) ATTRIBUTE_RETURN_CHECK;
char *virFindFileInPath(const char *file);
int virFileExists(const char *path);
-int virFileMakePath(const char *path); +int virFileMakePath(const char *path) ATTRIBUTE_RETURN_CHECK;
int virFileBuildPath(const char *dir, const char *name, const char *ext, char *buf, - unsigned int buflen); + unsigned int buflen) ATTRIBUTE_RETURN_CHECK;
int virFileAbsPath(const char *path, - char **abspath); + char **abspath) ATTRIBUTE_RETURN_CHECK;
int virFileOpenTty(int *ttymaster, char **ttyName, @@ -129,13 +129,13 @@ int virFileOpenTtyAt(const char *ptmx, char* virFilePid(const char *dir, const char *name); int virFileWritePidPath(const char *path, - pid_t pid); + pid_t pid) ATTRIBUTE_RETURN_CHECK; int virFileWritePid(const char *dir, const char *name, - pid_t pid); + pid_t pid) ATTRIBUTE_RETURN_CHECK; int virFileReadPid(const char *dir, const char *name, - pid_t *pid); + pid_t *pid) ATTRIBUTE_RETURN_CHECK; int virFileDeletePid(const char *dir, const char *name);
@@ -167,7 +167,7 @@ int virMacAddrCompare (const char *mac1, const char *mac2); void virSkipSpaces(const char **str); int virParseNumber(const char **str); int virAsprintf(char **strp, const char *fmt, ...) - ATTRIBUTE_FMT_PRINTF(2, 3); + ATTRIBUTE_FMT_PRINTF(2, 3) ATTRIBUTE_RETURN_CHECK; char *virStrncpy(char *dest, const char *src, size_t n, size_t destbytes) ATTRIBUTE_RETURN_CHECK; char *virStrcpy(char *dest, const char *src, size_t destbytes) @@ -179,7 +179,7 @@ char *virStrcpy(char *dest, const char *src, size_t destbytes) #define VIR_MAC_STRING_BUFLEN VIR_MAC_BUFLEN * 3
int virParseMacAddr(const char* str, - unsigned char *addr); + unsigned char *addr) ATTRIBUTE_RETURN_CHECK; void virFormatMacAddr(const unsigned char *addr, char *str); void virGenerateMacAddr(const unsigned char *prefix, @@ -233,13 +233,13 @@ char *virGetUserName(virConnectPtr conn, uid_t uid); int virGetUserID(virConnectPtr conn, const char *name, - uid_t *uid); + uid_t *uid) ATTRIBUTE_RETURN_CHECK; int virGetGroupID(virConnectPtr conn, const char *name, - gid_t *gid); + gid_t *gid) ATTRIBUTE_RETURN_CHECK; #endif
-int virRandomInitialize(unsigned int seed); +int virRandomInitialize(unsigned int seed) ATTRIBUTE_RETURN_CHECK; int virRandom(int max);
#ifdef HAVE_MNTENT_H diff --git a/src/vbox/vbox_tmpl.c b/src/vbox/vbox_tmpl.c index 4741c57..aecda23 100644 --- a/src/vbox/vbox_tmpl.c +++ b/src/vbox/vbox_tmpl.c @@ -2219,7 +2219,9 @@ static char *vboxDomainDumpXML(virDomainPtr dom, int flags) { MACAddress[4], MACAddress[5], MACAddress[6], MACAddress[7], MACAddress[8], MACAddress[9], MACAddress[10], MACAddress[11]);
- virParseMacAddr(macaddr, def->nets[netAdpIncCnt]->mac); + /* XXX some real error handling here some day ... */ + if (virParseMacAddr(macaddr, def->nets[netAdpIncCnt]->mac) < 0) + {}
netAdpIncCnt++;
diff --git a/src/xen/xm_internal.c b/src/xen/xm_internal.c index 732b2d3..b52f66e 100644 --- a/src/xen/xm_internal.c +++ b/src/xen/xm_internal.c @@ -3035,14 +3035,16 @@ xenXMDomainBlockPeek (virDomainPtr dom, static char *xenXMAutostartLinkName(virDomainPtr dom) { char *ret; - virAsprintf(&ret, "/etc/xen/auto/%s", dom->name); + if (virAsprintf(&ret, "/etc/xen/auto/%s", dom->name) < 0) + return NULL; return ret; }
static char *xenXMDomainConfigName(virDomainPtr dom) { char *ret; - virAsprintf(&ret, "/etc/xen/%s", dom->name); + if (virAsprintf(&ret, "/etc/xen/%s", dom->name) < 0) + return NULL; return ret; }
diff --git a/tests/qemuargv2xmltest.c b/tests/qemuargv2xmltest.c index 1b16aa9..5602df0 100644 --- a/tests/qemuargv2xmltest.c +++ b/tests/qemuargv2xmltest.c @@ -109,8 +109,6 @@ mymain(int argc, char **argv) if (!abs_srcdir) abs_srcdir = getcwd(cwd, sizeof(cwd));
- virRandomInitialize(0); - if ((driver.caps = testQemuCapsInit()) == NULL) return EXIT_FAILURE; if((driver.stateDir = strdup("/nowhere")) == NULL)
ACK ! This takes less time than running the OOM checks :-) 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/

* daemon/libvirtd.c: Change qemudNetworkInit() so that it doesn't try to free its argument, leaving the caller todo cleanup as is normal practice. Add missing policykit cleanup to qemudCleanup, and remove server watch if set. Remove duplicated call to listen() on TCP sockets --- daemon/libvirtd.c | 39 ++++++++++++++------------------------- 1 files changed, 14 insertions(+), 25 deletions(-) diff --git a/daemon/libvirtd.c b/daemon/libvirtd.c index fa473ce..db151cd 100644 --- a/daemon/libvirtd.c +++ b/daemon/libvirtd.c @@ -593,13 +593,7 @@ remoteMakeSockets (int *fds, int max_fds, int *nfds_r, const char *node, const c return -1; } close (fds[*nfds_r]); - } - else { - if (listen (fds[*nfds_r], SOMAXCONN) == -1) { - VIR_ERROR(_("listen: %s"), - virStrerror (errno, ebuf, sizeof ebuf)); - return -1; - } + } else { ++*nfds_r; } runp = runp->ai_next; @@ -867,8 +861,7 @@ static struct qemud_server *qemudInitialize(int sigread) { return server; } -static struct qemud_server *qemudNetworkInit(struct qemud_server *server) { - struct qemud_socket *sock; +static int qemudNetworkInit(struct qemud_server *server) { char sockname[PATH_MAX]; char roSockname[PATH_MAX]; #if HAVE_SASL @@ -935,6 +928,7 @@ static struct qemud_server *qemudNetworkInit(struct qemud_server *server) { #ifdef HAVE_AVAHI if (server->privileged && mdns_adv) { struct libvirtd_mdns_group *group; + struct qemud_socket *sock; int port = 0; server->mdns = libvirtd_mdns_new(); @@ -976,25 +970,13 @@ static struct qemud_server *qemudNetworkInit(struct qemud_server *server) { } #endif - return server; + return 0; cleanup: - if (server) { - sock = server->sockets; - while (sock) { - close(sock->fd); - sock = sock->next; - } - -#if HAVE_POLKIT0 - if (server->sysbus) - dbus_connection_unref(server->sysbus); -#endif - free(server); - } - return NULL; + return -1; } + static gnutls_session_t remoteInitializeTLSSession (void) { @@ -2305,6 +2287,8 @@ static void qemudCleanup(struct qemud_server *server) { sock = server->sockets; while (sock) { struct qemud_socket *next = sock->next; + if (sock->watch) + virEventRemoveHandleImpl(sock->watch); close(sock->fd); free(sock); sock = next; @@ -2322,6 +2306,11 @@ static void qemudCleanup(struct qemud_server *server) { } #endif +#if HAVE_POLKIT0 + if (server->sysbus) + dbus_connection_unref(server->sysbus); +#endif + virStateCleanup(); if (virCondDestroy(&server->job) < 0) { @@ -3015,7 +3004,7 @@ int main(int argc, char **argv) { goto error2; } - if (!(server = qemudNetworkInit(server))) { + if (qemudNetworkInit(server) < 0) { ret = 2; goto error2; } -- 1.6.2.5

On Fri, Oct 23, 2009 at 02:05:35PM +0100, Daniel P. Berrange wrote:
* daemon/libvirtd.c: Change qemudNetworkInit() so that it doesn't try to free its argument, leaving the caller todo cleanup as is normal practice. Add missing policykit cleanup to qemudCleanup, and remove server watch if set. Remove duplicated call to listen() on TCP sockets --- daemon/libvirtd.c | 39 ++++++++++++++------------------------- 1 files changed, 14 insertions(+), 25 deletions(-)
diff --git a/daemon/libvirtd.c b/daemon/libvirtd.c index fa473ce..db151cd 100644 --- a/daemon/libvirtd.c +++ b/daemon/libvirtd.c @@ -593,13 +593,7 @@ remoteMakeSockets (int *fds, int max_fds, int *nfds_r, const char *node, const c return -1; } close (fds[*nfds_r]); - } - else { - if (listen (fds[*nfds_r], SOMAXCONN) == -1) { - VIR_ERROR(_("listen: %s"), - virStrerror (errno, ebuf, sizeof ebuf)); - return -1; - } + } else { ++*nfds_r; } runp = runp->ai_next; @@ -867,8 +861,7 @@ static struct qemud_server *qemudInitialize(int sigread) { return server; }
-static struct qemud_server *qemudNetworkInit(struct qemud_server *server) { - struct qemud_socket *sock; +static int qemudNetworkInit(struct qemud_server *server) { char sockname[PATH_MAX]; char roSockname[PATH_MAX]; #if HAVE_SASL @@ -935,6 +928,7 @@ static struct qemud_server *qemudNetworkInit(struct qemud_server *server) { #ifdef HAVE_AVAHI if (server->privileged && mdns_adv) { struct libvirtd_mdns_group *group; + struct qemud_socket *sock; int port = 0;
server->mdns = libvirtd_mdns_new(); @@ -976,25 +970,13 @@ static struct qemud_server *qemudNetworkInit(struct qemud_server *server) { } #endif
- return server; + return 0;
cleanup: - if (server) { - sock = server->sockets; - while (sock) { - close(sock->fd); - sock = sock->next; - } - -#if HAVE_POLKIT0 - if (server->sysbus) - dbus_connection_unref(server->sysbus); -#endif - free(server); - } - return NULL; + return -1; }
+ static gnutls_session_t remoteInitializeTLSSession (void) { @@ -2305,6 +2287,8 @@ static void qemudCleanup(struct qemud_server *server) { sock = server->sockets; while (sock) { struct qemud_socket *next = sock->next; + if (sock->watch) + virEventRemoveHandleImpl(sock->watch); close(sock->fd); free(sock); sock = next; @@ -2322,6 +2306,11 @@ static void qemudCleanup(struct qemud_server *server) { } #endif
+#if HAVE_POLKIT0 + if (server->sysbus) + dbus_connection_unref(server->sysbus); +#endif + virStateCleanup();
if (virCondDestroy(&server->job) < 0) { @@ -3015,7 +3004,7 @@ int main(int argc, char **argv) { goto error2; }
- if (!(server = qemudNetworkInit(server))) { + if (qemudNetworkInit(server) < 0) { ret = 2; goto error2; }
It's rather hard to follow without seeing the source next to it, but that's a nice cleanup, 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/

virInitialize must be the first libvirt function called to ensure threads, error handling & random number generator are all setup. Move UNIX socket directory permissions change to place of use --- daemon/libvirtd.c | 21 ++++++++++----------- 1 files changed, 10 insertions(+), 11 deletions(-) diff --git a/daemon/libvirtd.c b/daemon/libvirtd.c index db151cd..252d527 100644 --- a/daemon/libvirtd.c +++ b/daemon/libvirtd.c @@ -700,9 +700,15 @@ static int qemudInitPaths(struct qemud_server *server, int ret = -1; char *sock_dir_prefix = NULL; - if (unix_sock_dir) + if (unix_sock_dir) { sock_dir = unix_sock_dir; - else { + /* Change the group ownership of /var/run/libvirt to unix_sock_gid */ + if (server->privileged) { + if (chown(unix_sock_dir, -1, unix_sock_gid) < 0) + VIR_ERROR(_("Failed to change group ownership of %s"), + unix_sock_dir); + } + } else { sock_dir = sockname; if (server->privileged) { dir_prefix = strdup (LOCAL_STATE_DIR); @@ -797,8 +803,6 @@ static struct qemud_server *qemudInitialize(int sigread) { return NULL; } - virInitialize(); - /* * Note that the order is important: the first ones have a higher * priority when calling virStateInitialize. We must register @@ -2848,6 +2852,8 @@ int main(int argc, char **argv) { {0, 0, 0, 0} }; + virInitialize(); + while (1) { int optidx = 0; int c; @@ -2988,13 +2994,6 @@ int main(int argc, char **argv) { if (remoteReadConfigFile (server, remote_config_file) < 0) goto error2; - /* Change the group ownership of /var/run/libvirt to unix_sock_gid */ - if (unix_sock_dir && server->privileged) { - if (chown(unix_sock_dir, -1, unix_sock_gid) < 0) - VIR_ERROR(_("Failed to change group ownership of %s"), - unix_sock_dir); - } - if (virEventAddHandleImpl(sigpipe[0], VIR_EVENT_HANDLE_READABLE, qemudDispatchSignalEvent, -- 1.6.2.5

On Fri, Oct 23, 2009 at 02:05:36PM +0100, Daniel P. Berrange wrote:
virInitialize must be the first libvirt function called to ensure threads, error handling & random number generator are all setup.
Move UNIX socket directory permissions change to place of use --- daemon/libvirtd.c | 21 ++++++++++----------- 1 files changed, 10 insertions(+), 11 deletions(-)
diff --git a/daemon/libvirtd.c b/daemon/libvirtd.c index db151cd..252d527 100644 --- a/daemon/libvirtd.c +++ b/daemon/libvirtd.c @@ -700,9 +700,15 @@ static int qemudInitPaths(struct qemud_server *server, int ret = -1; char *sock_dir_prefix = NULL;
- if (unix_sock_dir) + if (unix_sock_dir) { sock_dir = unix_sock_dir; - else { + /* Change the group ownership of /var/run/libvirt to unix_sock_gid */ + if (server->privileged) { + if (chown(unix_sock_dir, -1, unix_sock_gid) < 0) + VIR_ERROR(_("Failed to change group ownership of %s"), + unix_sock_dir); + } + } else { sock_dir = sockname; if (server->privileged) { dir_prefix = strdup (LOCAL_STATE_DIR); @@ -797,8 +803,6 @@ static struct qemud_server *qemudInitialize(int sigread) { return NULL; }
- virInitialize(); - /* * Note that the order is important: the first ones have a higher * priority when calling virStateInitialize. We must register @@ -2848,6 +2852,8 @@ int main(int argc, char **argv) { {0, 0, 0, 0} };
+ virInitialize(); + while (1) { int optidx = 0; int c; @@ -2988,13 +2994,6 @@ int main(int argc, char **argv) { if (remoteReadConfigFile (server, remote_config_file) < 0) goto error2;
- /* Change the group ownership of /var/run/libvirt to unix_sock_gid */ - if (unix_sock_dir && server->privileged) { - if (chown(unix_sock_dir, -1, unix_sock_gid) < 0) - VIR_ERROR(_("Failed to change group ownership of %s"), - unix_sock_dir); - } - if (virEventAddHandleImpl(sigpipe[0], VIR_EVENT_HANDLE_READABLE, qemudDispatchSignalEvent,
ACK makes sense, 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 libvirt default error handling callback will print all errors to stderr. The libvirtd default logging callback will do the same. Set a no-op error handling callback in libvirtd to prevent this duplication * daemon/libvirtd.c: Register a no-op error handling function --- daemon/libvirtd.c | 9 +++++++++ 1 files changed, 9 insertions(+), 0 deletions(-) diff --git a/daemon/libvirtd.c b/daemon/libvirtd.c index 252d527..ab0c88c 100644 --- a/daemon/libvirtd.c +++ b/daemon/libvirtd.c @@ -776,6 +776,12 @@ static int qemudInitPaths(struct qemud_server *server, return ret; } +static void virshErrorHandler(void *opaque ATTRIBUTE_UNUSED, virErrorPtr err ATTRIBUTE_UNUSED) +{ + /* Don't do anything, since logging infrastructure already + * took care of reporting the error */ +} + static struct qemud_server *qemudInitialize(int sigread) { struct qemud_server *server; @@ -2994,6 +3000,9 @@ int main(int argc, char **argv) { if (remoteReadConfigFile (server, remote_config_file) < 0) goto error2; + /* Disable error func, now logging is setup */ + virSetErrorFunc(NULL, virshErrorHandler); + if (virEventAddHandleImpl(sigpipe[0], VIR_EVENT_HANDLE_READABLE, qemudDispatchSignalEvent, -- 1.6.2.5

On Fri, Oct 23, 2009 at 02:05:37PM +0100, Daniel P. Berrange wrote:
The libvirt default error handling callback will print all errors to stderr. The libvirtd default logging callback will do the same. Set a no-op error handling callback in libvirtd to prevent this duplication
Argh :-) 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/

* daemon/libvirtd.c: Introduce a daemonSetupSignals() method and put all signal handling code there * daemon/libvirtd.h: Add sigread/sigwrite to qemud_server type --- daemon/libvirtd.c | 128 +++++++++++++++++++++++++++++++--------------------- daemon/libvirtd.h | 1 + 2 files changed, 77 insertions(+), 52 deletions(-) diff --git a/daemon/libvirtd.c b/daemon/libvirtd.c index ab0c88c..4e268a2 100644 --- a/daemon/libvirtd.c +++ b/daemon/libvirtd.c @@ -782,7 +782,7 @@ static void virshErrorHandler(void *opaque ATTRIBUTE_UNUSED, virErrorPtr err ATT * took care of reporting the error */ } -static struct qemud_server *qemudInitialize(int sigread) { +static struct qemud_server *qemudInitialize(void) { struct qemud_server *server; if (VIR_ALLOC(server) < 0) { @@ -790,21 +790,26 @@ static struct qemud_server *qemudInitialize(int sigread) { return NULL; } + server->privileged = geteuid() == 0 ? 1 : 0; + server->sigread = server->sigwrite = -1; + if (virMutexInit(&server->lock) < 0) { VIR_ERROR("%s", _("cannot initialize mutex")); VIR_FREE(server); + return NULL; } if (virCondInit(&server->job) < 0) { VIR_ERROR("%s", _("cannot initialize condition variable")); virMutexDestroy(&server->lock); VIR_FREE(server); + return NULL; } - server->privileged = geteuid() == 0 ? 1 : 0; - server->sigread = sigread; - if (virEventInit() < 0) { VIR_ERROR0(_("Failed to initialize event system")); + virMutexDestroy(&server->lock); + if (virCondDestroy(&server->job) < 0) + {} VIR_FREE(server); return NULL; } @@ -2292,7 +2297,10 @@ cleanup: static void qemudCleanup(struct qemud_server *server) { struct qemud_socket *sock; - close(server->sigread); + if (server->sigread != -1) + close(server->sigread); + if (server->sigwrite != -1) + close(server->sigwrite); sock = server->sockets; while (sock) { @@ -2787,6 +2795,58 @@ qemudSetupPrivs (void) #define qemudSetupPrivs() 0 #endif +static int +daemonSetupSignals(struct qemud_server *server) +{ + struct sigaction sig_action; + int sigpipe[2]; + + if (pipe(sigpipe) < 0) + return -1; + + if (virSetNonBlock(sigpipe[0]) < 0 || + virSetNonBlock(sigpipe[1]) < 0 || + virSetCloseExec(sigpipe[0]) < 0 || + virSetCloseExec(sigpipe[1]) < 0) { + char ebuf[1024]; + VIR_ERROR(_("Failed to create pipe: %s"), + virStrerror(errno, ebuf, sizeof ebuf)); + goto error; + } + + sig_action.sa_sigaction = sig_handler; + sig_action.sa_flags = SA_SIGINFO; + sigemptyset(&sig_action.sa_mask); + + sigaction(SIGHUP, &sig_action, NULL); + sigaction(SIGINT, &sig_action, NULL); + sigaction(SIGQUIT, &sig_action, NULL); + sigaction(SIGTERM, &sig_action, NULL); + sigaction(SIGCHLD, &sig_action, NULL); + + sig_action.sa_handler = SIG_IGN; + sigaction(SIGPIPE, &sig_action, NULL); + + if (virEventAddHandleImpl(sigpipe[0], + VIR_EVENT_HANDLE_READABLE, + qemudDispatchSignalEvent, + server, NULL) < 0) { + VIR_ERROR0(_("Failed to register callback for signal pipe")); + goto error; + } + + server->sigread = sigpipe[0]; + server->sigwrite = sigpipe[1]; + sigwrite = sigpipe[1]; + + return 0; + +error: + close(sigpipe[0]); + close(sigpipe[1]); + return -1; +} + /* Print command-line usage. */ static void usage (const char *argv0) @@ -2840,8 +2900,6 @@ enum { #define MAX_LISTEN 5 int main(int argc, char **argv) { struct qemud_server *server = NULL; - struct sigaction sig_action; - int sigpipe[2]; const char *pid_file = NULL; const char *remote_config_file = NULL; int ret = 1; @@ -2930,7 +2988,7 @@ int main(int argc, char **argv) { if (qemudGoDaemon() < 0) { VIR_ERROR(_("Failed to fork as daemon: %s"), virStrerror(errno, ebuf, sizeof ebuf)); - goto error1; + goto error; } } @@ -2943,32 +3001,7 @@ int main(int argc, char **argv) { /* If we have a pidfile set, claim it now, exiting if already taken */ if (pid_file != NULL && qemudWritePidFile (pid_file) < 0) - goto error1; - - if (pipe(sigpipe) < 0 || - virSetNonBlock(sigpipe[0]) < 0 || - virSetNonBlock(sigpipe[1]) < 0 || - virSetCloseExec(sigpipe[0]) < 0 || - virSetCloseExec(sigpipe[1]) < 0) { - char ebuf[1024]; - VIR_ERROR(_("Failed to create pipe: %s"), - virStrerror(errno, ebuf, sizeof ebuf)); - goto error2; - } - sigwrite = sigpipe[1]; - - sig_action.sa_sigaction = sig_handler; - sig_action.sa_flags = SA_SIGINFO; - sigemptyset(&sig_action.sa_mask); - - sigaction(SIGHUP, &sig_action, NULL); - sigaction(SIGINT, &sig_action, NULL); - sigaction(SIGQUIT, &sig_action, NULL); - sigaction(SIGTERM, &sig_action, NULL); - sigaction(SIGCHLD, &sig_action, NULL); - - sig_action.sa_handler = SIG_IGN; - sigaction(SIGPIPE, &sig_action, NULL); + goto error; /* Ensure the rundir exists (on tmpfs on some systems) */ if (geteuid() == 0) { @@ -2989,46 +3022,37 @@ int main(int argc, char **argv) { * drivers */ if (qemudSetupPrivs() < 0) - goto error2; + goto error; - if (!(server = qemudInitialize(sigpipe[0]))) { + if (!(server = qemudInitialize())) { ret = 2; - goto error2; + goto error; } + if ((daemonSetupSignals(server)) < 0) + goto error; + /* Read the config file (if it exists). */ if (remoteReadConfigFile (server, remote_config_file) < 0) - goto error2; + goto error; /* Disable error func, now logging is setup */ virSetErrorFunc(NULL, virshErrorHandler); - if (virEventAddHandleImpl(sigpipe[0], - VIR_EVENT_HANDLE_READABLE, - qemudDispatchSignalEvent, - server, NULL) < 0) { - VIR_ERROR0(_("Failed to register callback for signal pipe")); - ret = 3; - goto error2; - } - if (qemudNetworkInit(server) < 0) { ret = 2; - goto error2; + goto error; } qemudRunLoop(server); ret = 0; -error2: +error: if (server) qemudCleanup(server); if (pid_file) unlink (pid_file); - close(sigwrite); - -error1: virLogShutdown(); return ret; } diff --git a/daemon/libvirtd.h b/daemon/libvirtd.h index 579e1c4..c0784d8 100644 --- a/daemon/libvirtd.h +++ b/daemon/libvirtd.h @@ -267,6 +267,7 @@ struct qemud_server { struct qemud_client **clients; int sigread; + int sigwrite; char *logDir; unsigned int shutdown : 1; #ifdef HAVE_AVAHI -- 1.6.2.5

On Fri, Oct 23, 2009 at 02:05:38PM +0100, Daniel P. Berrange wrote:
* daemon/libvirtd.c: Introduce a daemonSetupSignals() method and put all signal handling code there * daemon/libvirtd.h: Add sigread/sigwrite to qemud_server type --- daemon/libvirtd.c | 128 +++++++++++++++++++++++++++++++--------------------- daemon/libvirtd.h | 1 + 2 files changed, 77 insertions(+), 52 deletions(-)
diff --git a/daemon/libvirtd.c b/daemon/libvirtd.c index ab0c88c..4e268a2 100644 --- a/daemon/libvirtd.c +++ b/daemon/libvirtd.c @@ -782,7 +782,7 @@ static void virshErrorHandler(void *opaque ATTRIBUTE_UNUSED, virErrorPtr err ATT * took care of reporting the error */ }
-static struct qemud_server *qemudInitialize(int sigread) { +static struct qemud_server *qemudInitialize(void) { struct qemud_server *server;
if (VIR_ALLOC(server) < 0) { @@ -790,21 +790,26 @@ static struct qemud_server *qemudInitialize(int sigread) { return NULL; }
+ server->privileged = geteuid() == 0 ? 1 : 0; + server->sigread = server->sigwrite = -1; + if (virMutexInit(&server->lock) < 0) { VIR_ERROR("%s", _("cannot initialize mutex")); VIR_FREE(server); + return NULL; } if (virCondInit(&server->job) < 0) { VIR_ERROR("%s", _("cannot initialize condition variable")); virMutexDestroy(&server->lock); VIR_FREE(server); + return NULL; }
- server->privileged = geteuid() == 0 ? 1 : 0; - server->sigread = sigread; - if (virEventInit() < 0) { VIR_ERROR0(_("Failed to initialize event system")); + virMutexDestroy(&server->lock); + if (virCondDestroy(&server->job) < 0) + {}
Hum ... but is there really anything we can do there ?
VIR_FREE(server); return NULL; } @@ -2292,7 +2297,10 @@ cleanup: static void qemudCleanup(struct qemud_server *server) { struct qemud_socket *sock;
- close(server->sigread); + if (server->sigread != -1) + close(server->sigread); + if (server->sigwrite != -1) + close(server->sigwrite);
sock = server->sockets; while (sock) { @@ -2787,6 +2795,58 @@ qemudSetupPrivs (void) #define qemudSetupPrivs() 0 #endif
+static int +daemonSetupSignals(struct qemud_server *server) +{ + struct sigaction sig_action; + int sigpipe[2]; + + if (pipe(sigpipe) < 0) + return -1; + + if (virSetNonBlock(sigpipe[0]) < 0 || + virSetNonBlock(sigpipe[1]) < 0 || + virSetCloseExec(sigpipe[0]) < 0 || + virSetCloseExec(sigpipe[1]) < 0) { + char ebuf[1024]; + VIR_ERROR(_("Failed to create pipe: %s"), + virStrerror(errno, ebuf, sizeof ebuf)); + goto error; + } + + sig_action.sa_sigaction = sig_handler; + sig_action.sa_flags = SA_SIGINFO; + sigemptyset(&sig_action.sa_mask); + + sigaction(SIGHUP, &sig_action, NULL); + sigaction(SIGINT, &sig_action, NULL); + sigaction(SIGQUIT, &sig_action, NULL); + sigaction(SIGTERM, &sig_action, NULL); + sigaction(SIGCHLD, &sig_action, NULL); + + sig_action.sa_handler = SIG_IGN; + sigaction(SIGPIPE, &sig_action, NULL); + + if (virEventAddHandleImpl(sigpipe[0], + VIR_EVENT_HANDLE_READABLE, + qemudDispatchSignalEvent, + server, NULL) < 0) { + VIR_ERROR0(_("Failed to register callback for signal pipe")); + goto error; + } + + server->sigread = sigpipe[0]; + server->sigwrite = sigpipe[1]; + sigwrite = sigpipe[1]; + + return 0; + +error: + close(sigpipe[0]); + close(sigpipe[1]); + return -1; +} +
ACK, this is something not trivial to understand isolating it is a good idea, and with a comment it would be perfect :-) 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 daemonizing code lets the parent exit almost immediately. This means that it may think it has successfully started even when important failures occur like not being able to acquire the PID file. It also means network sockets are not yet open. To address this when daemonizing the parent passes an open pipe file descriptor to the child. The child does its basic initialization and then writes a status code to the pipe indicating either success, or failure. This ensures that when daemonizing, the parent does not exit until the pidfile is acquired & basic network sockets are open. Initialization of the libvirt drivers is still done asynchronously since this may take a very long time. * daemon/libvirtd.c: Force parent to stay around until basic config file, pidfile & network socket init is completed --- daemon/libvirtd.c | 120 +++++++++++++++++++++++++++++++++++++++++++++-------- 1 files changed, 102 insertions(+), 18 deletions(-) diff --git a/daemon/libvirtd.c b/daemon/libvirtd.c index 4e268a2..b118da8 100644 --- a/daemon/libvirtd.c +++ b/daemon/libvirtd.c @@ -185,6 +185,30 @@ static int max_client_requests = 5; static sig_atomic_t sig_errors = 0; static int sig_lasterrno = 0; +enum { + VIR_DAEMON_ERR_NONE = 0, + VIR_DAEMON_ERR_PIDFILE, + VIR_DAEMON_ERR_RUNDIR, + VIR_DAEMON_ERR_INIT, + VIR_DAEMON_ERR_SIGNAL, + VIR_DAEMON_ERR_PRIVS, + VIR_DAEMON_ERR_NETWORK, + VIR_DAEMON_ERR_CONFIG, + + VIR_DAEMON_ERR_LAST +}; + +VIR_ENUM_DECL(virDaemonErr) +VIR_ENUM_IMPL(virDaemonErr, VIR_DAEMON_ERR_LAST, + "Initialization successful", + "Unable to obtain pidfile", + "Unable to create rundir", + "Unable to initialize libvirt", + "Unable to setup signal handlers", + "Unable to drop privileges", + "Unable to initialize network sockets", + "Unable to load configuration file") + static void sig_handler(int sig, siginfo_t * siginfo, void* context ATTRIBUTE_UNUSED) { int origerrno; @@ -375,7 +399,11 @@ qemudDispatchSignalEvent(int watch ATTRIBUTE_UNUSED, } -static int qemudGoDaemon(void) { +static int daemonForkIntoBackground(void) { + int statuspipe[2]; + if (pipe(statuspipe) < 0) + return -1; + int pid = fork(); switch (pid) { case 0: @@ -384,6 +412,8 @@ static int qemudGoDaemon(void) { int stdoutfd = -1; int nextpid; + close(statuspipe[0]); + if ((stdinfd = open("/dev/null", O_RDONLY)) < 0) goto cleanup; if ((stdoutfd = open("/dev/null", O_WRONLY)) < 0) @@ -407,7 +437,7 @@ static int qemudGoDaemon(void) { nextpid = fork(); switch (nextpid) { case 0: - return 0; + return statuspipe[1]; case -1: return -1; default: @@ -428,15 +458,29 @@ static int qemudGoDaemon(void) { default: { - int got, status = 0; - /* We wait to make sure the next child forked - successfully */ - if ((got = waitpid(pid, &status, 0)) < 0 || + int got, exitstatus = 0; + int ret; + char status; + + close(statuspipe[1]); + + /* We wait to make sure the first child forked successfully */ + if ((got = waitpid(pid, &exitstatus, 0)) < 0 || got != pid || status != 0) { return -1; } - _exit(0); + + /* Now block until the second child initializes successfully */ + again: + ret = read(statuspipe[0], &status, 1); + if (ret == -1 && errno == EINTR) + goto again; + + if (ret == 1 && status != 0) { + fprintf(stderr, "error: %s\n", virDaemonErrTypeToString(status)); + } + _exit(ret == 1 && status == 0 ? 0 : 1); } } } @@ -871,8 +915,6 @@ static struct qemud_server *qemudInitialize(void) { virEventUpdateTimeoutImpl, virEventRemoveTimeoutImpl); - virStateInitialize(server->privileged); - return server; } @@ -2902,6 +2944,7 @@ int main(int argc, char **argv) { struct qemud_server *server = NULL; const char *pid_file = NULL; const char *remote_config_file = NULL; + int statuswrite = -1; int ret = 1; struct option opts[] = { @@ -2985,7 +3028,7 @@ int main(int argc, char **argv) { if (godaemon) { char ebuf[1024]; - if (qemudGoDaemon() < 0) { + if ((statuswrite = daemonForkIntoBackground()) < 0) { VIR_ERROR(_("Failed to fork as daemon: %s"), virStrerror(errno, ebuf, sizeof ebuf)); goto error; @@ -3000,8 +3043,11 @@ int main(int argc, char **argv) { /* If we have a pidfile set, claim it now, exiting if already taken */ if (pid_file != NULL && - qemudWritePidFile (pid_file) < 0) + qemudWritePidFile (pid_file) < 0) { + pid_file = NULL; /* Prevent unlinking of someone else's pid ! */ + ret = VIR_DAEMON_ERR_PIDFILE; goto error; + } /* Ensure the rundir exists (on tmpfs on some systems) */ if (geteuid() == 0) { @@ -3010,7 +3056,8 @@ int main(int argc, char **argv) { if (mkdir (rundir, 0755)) { if (errno != EEXIST) { VIR_ERROR0 (_("unable to create rundir")); - return -1; + ret = VIR_DAEMON_ERR_RUNDIR; + goto error; } } } @@ -3021,34 +3068,71 @@ int main(int argc, char **argv) { * which is also passed into all libvirt stateful * drivers */ - if (qemudSetupPrivs() < 0) + if (qemudSetupPrivs() < 0) { + ret = VIR_DAEMON_ERR_PRIVS; goto error; + } if (!(server = qemudInitialize())) { - ret = 2; + ret = VIR_DAEMON_ERR_INIT; goto error; } - if ((daemonSetupSignals(server)) < 0) + if ((daemonSetupSignals(server)) < 0) { + ret = VIR_DAEMON_ERR_SIGNAL; goto error; + } /* Read the config file (if it exists). */ - if (remoteReadConfigFile (server, remote_config_file) < 0) + if (remoteReadConfigFile (server, remote_config_file) < 0) { + ret = VIR_DAEMON_ERR_CONFIG; goto error; + } /* Disable error func, now logging is setup */ virSetErrorFunc(NULL, virshErrorHandler); if (qemudNetworkInit(server) < 0) { - ret = 2; + ret = VIR_DAEMON_ERR_NETWORK; goto error; } - qemudRunLoop(server); + /* Tell parent of daemon that basic initialization is complete + * In particular we're ready to accept net connections & have + * written the pidfile + */ + if (statuswrite != -1) { + char status = 0; + while (write(statuswrite, &status, 1) == -1 && + errno == EINTR) + ; + close(statuswrite); + statuswrite = -1; + } + + /* Start the stateful HV drivers + * This is delibrately done after telling the parent process + * we're ready, since it can take a long time and this will + * seriously delay OS bootup process */ + if (virStateInitialize(server->privileged) < 0) { + VIR_ERROR0("Driver state initialization failed"); + goto error; + } + qemudRunLoop(server); ret = 0; error: + if (statuswrite != -1) { + if (ret != 0) { + /* Tell parent of daemon what failed */ + char status = ret; + while (write(statuswrite, &status, 1) == -1 && + errno == EINTR) + ; + } + close(statuswrite); + } if (server) qemudCleanup(server); if (pid_file) -- 1.6.2.5

Hi Daniel, On Fri, Oct 23, 2009 at 02:05:39PM +0100, Daniel P. Berrange wrote:
The daemonizing code lets the parent exit almost immediately. This means that it may think it has successfully started even when important failures occur like not being able to acquire the PID file. It also means network sockets are not yet open. With this patch applied to 0.7.2 I'm seeing:
# libvirtd -d 15:20:22.457: error : main:2971 : Failed to fork as daemon: Success about every second time I start the daemon. Any idea where this comes from? Cheers, -- Guido

On Wed, Oct 28, 2009 at 03:22:38PM +0100, Guido G?nther wrote:
Hi Daniel, On Fri, Oct 23, 2009 at 02:05:39PM +0100, Daniel P. Berrange wrote:
The daemonizing code lets the parent exit almost immediately. This means that it may think it has successfully started even when important failures occur like not being able to acquire the PID file. It also means network sockets are not yet open. With this patch applied to 0.7.2 I'm seeing:
# libvirtd -d 15:20:22.457: error : main:2971 : Failed to fork as daemon: Success
about every second time I start the daemon. Any idea where this comes from?
No, but that sounds bad to me - something I'm not expecting must be going on here. Definitely sounds like a race condition. 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 :|

On Wed, Oct 28, 2009 at 03:22:38PM +0100, Guido G?nther wrote:
Hi Daniel, On Fri, Oct 23, 2009 at 02:05:39PM +0100, Daniel P. Berrange wrote:
The daemonizing code lets the parent exit almost immediately. This means that it may think it has successfully started even when important failures occur like not being able to acquire the PID file. It also means network sockets are not yet open. With this patch applied to 0.7.2 I'm seeing:
# libvirtd -d 15:20:22.457: error : main:2971 : Failed to fork as daemon: Success
about every second time I start the daemon. Any idea where this comes from?
No, but that sounds bad to me - something I'm not expecting must be going on here. Definitely sounds like a race condition. I should add that the daemon is running fine afterwards. It's just that
On Wed, Oct 28, 2009 at 06:52:49PM +0000, Daniel P. Berrange wrote: the parent believes it doesn't. Cheers, -- Guido

On Fri, Oct 23, 2009 at 02:05:39PM +0100, Daniel P. Berrange wrote:
The daemonizing code lets the parent exit almost immediately. This means that it may think it has successfully started even when important failures occur like not being able to acquire the PID file. It also means network sockets are not yet open.
To address this when daemonizing the parent passes an open pipe file descriptor to the child. The child does its basic initialization and then writes a status code to the pipe indicating either success, or failure. This ensures that when daemonizing, the parent does not exit until the pidfile is acquired & basic network sockets are open.
Initialization of the libvirt drivers is still done asynchronously since this may take a very long time.
Hum, on the other hand maybe it's still better to wait until having gone though drivers inits, basically this affects boot time or service (re) start time, but maybe it's better to give hand when the service is actually started and ready to get connections. With the parallel boots only services dependant on libvirt may have to wait a long time, and maybe it's better that way.
* daemon/libvirtd.c: Force parent to stay around until basic config file, pidfile & network socket init is completed
devil is in the details, the code looks okay, but apparently there is something to iron out. 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 Thu, Oct 29, 2009 at 04:19:47PM +0100, Daniel Veillard wrote:
On Fri, Oct 23, 2009 at 02:05:39PM +0100, Daniel P. Berrange wrote:
The daemonizing code lets the parent exit almost immediately. This means that it may think it has successfully started even when important failures occur like not being able to acquire the PID file. It also means network sockets are not yet open.
To address this when daemonizing the parent passes an open pipe file descriptor to the child. The child does its basic initialization and then writes a status code to the pipe indicating either success, or failure. This ensures that when daemonizing, the parent does not exit until the pidfile is acquired & basic network sockets are open.
Initialization of the libvirt drivers is still done asynchronously since this may take a very long time.
Hum, on the other hand maybe it's still better to wait until having gone though drivers inits, basically this affects boot time or service (re) start time, but maybe it's better to give hand when the service is actually started and ready to get connections. With the parallel boots only services dependant on libvirt may have to wait a long time, and maybe it's better that way.
We used to block on driver initialization, but its not really viable. It can take a *very* long time - many minutes if auto-starting lots of VMs. By initializing everything except the drivers, apps depending on libvirt can reliably connect, but their RPC calls won't be processed until we finish driver init. So we effectively block dependant things, without impacting on the OS startup process 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 :|

On Thu, Oct 29, 2009 at 03:29:38PM +0000, Daniel P. Berrange wrote:
On Thu, Oct 29, 2009 at 04:19:47PM +0100, Daniel Veillard wrote:
On Fri, Oct 23, 2009 at 02:05:39PM +0100, Daniel P. Berrange wrote:
The daemonizing code lets the parent exit almost immediately. This means that it may think it has successfully started even when important failures occur like not being able to acquire the PID file. It also means network sockets are not yet open.
To address this when daemonizing the parent passes an open pipe file descriptor to the child. The child does its basic initialization and then writes a status code to the pipe indicating either success, or failure. This ensures that when daemonizing, the parent does not exit until the pidfile is acquired & basic network sockets are open.
Initialization of the libvirt drivers is still done asynchronously since this may take a very long time.
Hum, on the other hand maybe it's still better to wait until having gone though drivers inits, basically this affects boot time or service (re) start time, but maybe it's better to give hand when the service is actually started and ready to get connections. With the parallel boots only services dependant on libvirt may have to wait a long time, and maybe it's better that way.
We used to block on driver initialization, but its not really viable. It can take a *very* long time - many minutes if auto-starting lots of VMs.
By initializing everything except the drivers, apps depending on libvirt can reliably connect, but their RPC calls won't be processed until we finish driver init. So we effectively block dependant things, without impacting on the OS startup process
okay then ! 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 10/23/2009 09:05 AM, Daniel P. Berrange wrote:
The daemonizing code lets the parent exit almost immediately. This means that it may think it has successfully started even when important failures occur like not being able to acquire the PID file. It also means network sockets are not yet open.
To address this when daemonizing the parent passes an open pipe file descriptor to the child. The child does its basic initialization and then writes a status code to the pipe indicating either success, or failure. This ensures that when daemonizing, the parent does not exit until the pidfile is acquired & basic network sockets are open.
Initialization of the libvirt drivers is still done asynchronously since this may take a very long time.
* daemon/libvirtd.c: Force parent to stay around until basic config file, pidfile & network socket init is completed --- daemon/libvirtd.c | 120 +++++++++++++++++++++++++++++++++++++++++++++-------- 1 files changed, 102 insertions(+), 18 deletions(-)
diff --git a/daemon/libvirtd.c b/daemon/libvirtd.c index 4e268a2..b118da8 100644 --- a/daemon/libvirtd.c +++ b/daemon/libvirtd.c @@ -185,6 +185,30 @@ static int max_client_requests = 5; static sig_atomic_t sig_errors = 0; static int sig_lasterrno = 0;
+enum { + VIR_DAEMON_ERR_NONE = 0, + VIR_DAEMON_ERR_PIDFILE, + VIR_DAEMON_ERR_RUNDIR, + VIR_DAEMON_ERR_INIT, + VIR_DAEMON_ERR_SIGNAL, + VIR_DAEMON_ERR_PRIVS, + VIR_DAEMON_ERR_NETWORK, + VIR_DAEMON_ERR_CONFIG, + + VIR_DAEMON_ERR_LAST +}; + +VIR_ENUM_DECL(virDaemonErr) +VIR_ENUM_IMPL(virDaemonErr, VIR_DAEMON_ERR_LAST, + "Initialization successful", + "Unable to obtain pidfile", + "Unable to create rundir", + "Unable to initialize libvirt", + "Unable to setup signal handlers", + "Unable to drop privileges", + "Unable to initialize network sockets", + "Unable to load configuration file") + static void sig_handler(int sig, siginfo_t * siginfo, void* context ATTRIBUTE_UNUSED) { int origerrno; @@ -375,7 +399,11 @@ qemudDispatchSignalEvent(int watch ATTRIBUTE_UNUSED, }
-static int qemudGoDaemon(void) { +static int daemonForkIntoBackground(void) { + int statuspipe[2]; + if (pipe(statuspipe) < 0) + return -1; + int pid = fork(); switch (pid) { case 0: @@ -384,6 +412,8 @@ static int qemudGoDaemon(void) { int stdoutfd = -1; int nextpid;
+ close(statuspipe[0]); + if ((stdinfd = open("/dev/null", O_RDONLY)) < 0) goto cleanup; if ((stdoutfd = open("/dev/null", O_WRONLY)) < 0) @@ -407,7 +437,7 @@ static int qemudGoDaemon(void) { nextpid = fork(); switch (nextpid) { case 0: - return 0; + return statuspipe[1]; case -1: return -1; default: @@ -428,15 +458,29 @@ static int qemudGoDaemon(void) {
default: { - int got, status = 0; - /* We wait to make sure the next child forked - successfully */ - if ((got = waitpid(pid, &status, 0)) < 0 || + int got, exitstatus = 0; + int ret; + char status; + + close(statuspipe[1]); + + /* We wait to make sure the first child forked successfully */ + if ((got = waitpid(pid, &exitstatus, 0)) < 0 || got != pid || status != 0) { return -1; } - _exit(0); + + /* Now block until the second child initializes successfully */ + again: + ret = read(statuspipe[0], &status, 1); + if (ret == -1 && errno == EINTR) + goto again; + + if (ret == 1 && status != 0) { + fprintf(stderr, "error: %s\n", virDaemonErrTypeToString(status)); + } + _exit(ret == 1 && status == 0 ? 0 : 1); } } } @@ -871,8 +915,6 @@ static struct qemud_server *qemudInitialize(void) { virEventUpdateTimeoutImpl, virEventRemoveTimeoutImpl);
- virStateInitialize(server->privileged); - return server; }
@@ -2902,6 +2944,7 @@ int main(int argc, char **argv) { struct qemud_server *server = NULL; const char *pid_file = NULL; const char *remote_config_file = NULL; + int statuswrite = -1; int ret = 1;
struct option opts[] = { @@ -2985,7 +3028,7 @@ int main(int argc, char **argv) {
if (godaemon) { char ebuf[1024]; - if (qemudGoDaemon() < 0) { + if ((statuswrite = daemonForkIntoBackground()) < 0) { VIR_ERROR(_("Failed to fork as daemon: %s"), virStrerror(errno, ebuf, sizeof ebuf)); goto error; @@ -3000,8 +3043,11 @@ int main(int argc, char **argv) {
/* If we have a pidfile set, claim it now, exiting if already taken */ if (pid_file != NULL && - qemudWritePidFile (pid_file) < 0) + qemudWritePidFile (pid_file) < 0) { + pid_file = NULL; /* Prevent unlinking of someone else's pid ! */ + ret = VIR_DAEMON_ERR_PIDFILE; goto error; + }
/* Ensure the rundir exists (on tmpfs on some systems) */ if (geteuid() == 0) { @@ -3010,7 +3056,8 @@ int main(int argc, char **argv) { if (mkdir (rundir, 0755)) { if (errno != EEXIST) { VIR_ERROR0 (_("unable to create rundir")); - return -1; + ret = VIR_DAEMON_ERR_RUNDIR; + goto error; } } } @@ -3021,34 +3068,71 @@ int main(int argc, char **argv) { * which is also passed into all libvirt stateful * drivers */ - if (qemudSetupPrivs() < 0) + if (qemudSetupPrivs() < 0) { + ret = VIR_DAEMON_ERR_PRIVS; goto error; + }
if (!(server = qemudInitialize())) { - ret = 2; + ret = VIR_DAEMON_ERR_INIT; goto error; }
- if ((daemonSetupSignals(server)) < 0) + if ((daemonSetupSignals(server)) < 0) { + ret = VIR_DAEMON_ERR_SIGNAL; goto error; + }
/* Read the config file (if it exists). */ - if (remoteReadConfigFile (server, remote_config_file) < 0) + if (remoteReadConfigFile (server, remote_config_file) < 0) { + ret = VIR_DAEMON_ERR_CONFIG; goto error; + }
/* Disable error func, now logging is setup */ virSetErrorFunc(NULL, virshErrorHandler);
if (qemudNetworkInit(server) < 0) { - ret = 2; + ret = VIR_DAEMON_ERR_NETWORK; goto error; }
- qemudRunLoop(server); + /* Tell parent of daemon that basic initialization is complete + * In particular we're ready to accept net connections & have + * written the pidfile + */ + if (statuswrite != -1) { + char status = 0; + while (write(statuswrite, &status, 1) == -1 && + errno == EINTR) + ; + close(statuswrite); + statuswrite = -1; + } + + /* Start the stateful HV drivers + * This is delibrately done after telling the parent process + * we're ready, since it can take a long time and this will + * seriously delay OS bootup process */ + if (virStateInitialize(server->privileged) < 0) { + VIR_ERROR0("Driver state initialization failed"); + goto error; + }
This breaks qemu:///session for me. Starting libvirtd by hand as a regular user, virStateInitialize tries to init lxc, but lxc explicitly denies non-root driver startup in lxc_driver.c:lxcStartup: /* Check that the user is root */ if (!privileged) { return -1; } Not sure what the proper fix is. - Cole

On Mon, Nov 02, 2009 at 05:05:55PM -0500, Cole Robinson wrote:
On 10/23/2009 09:05 AM, Daniel P. Berrange wrote:
The daemonizing code lets the parent exit almost immediately. This means that it may think it has successfully started even when important failures occur like not being able to acquire the PID file. It also means network sockets are not yet open.
To address this when daemonizing the parent passes an open pipe file descriptor to the child. The child does its basic initialization and then writes a status code to the pipe indicating either success, or failure. This ensures that when daemonizing, the parent does not exit until the pidfile is acquired & basic network sockets are open.
Initialization of the libvirt drivers is still done asynchronously since this may take a very long time.
* daemon/libvirtd.c: Force parent to stay around until basic config file, pidfile & network socket init is completed
+ /* Start the stateful HV drivers + * This is delibrately done after telling the parent process + * we're ready, since it can take a long time and this will + * seriously delay OS bootup process */ + if (virStateInitialize(server->privileged) < 0) { + VIR_ERROR0("Driver state initialization failed"); + goto error; + }
This breaks qemu:///session for me.
Starting libvirtd by hand as a regular user, virStateInitialize tries to init lxc, but lxc explicitly denies non-root driver startup in lxc_driver.c:lxcStartup:
/* Check that the user is root */ if (!privileged) { return -1; }
Not sure what the proper fix is.
Sorry, this was a rebase messup. The fix for this is in the next patch in the series - lxcStartup() should only return -1 for actual errors. Running unprivileged is not an error, it should merely disable itself and return 0. I'll apply the fix now. 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 :|

The virStateInitialize() call for starting up stateful drivers may require that the event loop is running already. This it is neccessary to start the event loop before this call. At the same time, network clients must not be processed until afte virStateInitialize has completed. The qemudListenUnix() and remoteListenTCP() methods must therefore not register file handle watches, merely open the network sockets & listen() on them. This means clients can connected and are queued, pending completion of initialization The qemudRunLoop() method is moved into a background thread that is started early to allow access to the event loop during driver initialization. The main process thread leader pretty much does nothing once the daemon is running, merely waits for the event loop thread to quit * daemon/libvirtd.c, daemon/libvirtd.h: Move event loop into a background thread * src/driver.h: Add a 'name' field to state driver to allow easy identification during failures * src/libvirt.c: Log name of failed driver for virStateInit failures * src/lxc/lxc_driver.c: Don't return a failure code for lxcStartup() if LXC is not available on this host, simply disable the driver. * src/network/bridge_driver.c, src/node_device/node_device_devkit.c, src/node_device/node_device_hal.c, src/opennebula/one_driver.c, src/qemu/qemu_driver.c, src/remote/remote_driver.c, src/secret/secret_driver.c, src/storage/storage_driver.c, src/uml/uml_driver.c, src/xen/xen_driver.c: Fill in name field in virStateDriver struct --- daemon/libvirtd.c | 115 +++++++++++++++++++++++----------- daemon/libvirtd.h | 4 +- src/driver.h | 1 + src/libvirt.c | 5 +- src/lxc/lxc_driver.c | 24 ++++--- src/network/bridge_driver.c | 1 + src/node_device/node_device_devkit.c | 1 + src/node_device/node_device_hal.c | 1 + src/opennebula/one_driver.c | 1 + src/qemu/qemu_driver.c | 1 + src/remote/remote_driver.c | 1 + src/secret/secret_driver.c | 1 + src/storage/storage_driver.c | 1 + src/uml/uml_driver.c | 1 + src/xen/xen_driver.c | 1 + 15 files changed, 111 insertions(+), 48 deletions(-) diff --git a/daemon/libvirtd.c b/daemon/libvirtd.c index b118da8..a20bbb8 100644 --- a/daemon/libvirtd.c +++ b/daemon/libvirtd.c @@ -384,7 +384,7 @@ qemudDispatchSignalEvent(int watch ATTRIBUTE_UNUSED, case SIGQUIT: case SIGTERM: VIR_WARN(_("Shutting down on signal %d"), siginfo.si_signo); - server->shutdown = 1; + server->quitEventThread = 1; break; default: @@ -393,7 +393,7 @@ qemudDispatchSignalEvent(int watch ATTRIBUTE_UNUSED, } if (ret != 0) - server->shutdown = 1; + server->quitEventThread = 1; virMutexUnlock(&server->lock); } @@ -579,16 +579,6 @@ static int qemudListenUnix(struct qemud_server *server, goto cleanup; } - if ((sock->watch = virEventAddHandleImpl(sock->fd, - VIR_EVENT_HANDLE_READABLE | - VIR_EVENT_HANDLE_ERROR | - VIR_EVENT_HANDLE_HANGUP, - qemudDispatchServerEvent, - server, NULL)) < 0) { - VIR_ERROR0(_("Failed to add server event callback")); - goto cleanup; - } - sock->next = server->sockets; server->sockets = sock; server->nsockets++; @@ -713,17 +703,6 @@ remoteListenTCP (struct qemud_server *server, virStrerror (errno, ebuf, sizeof ebuf)); goto cleanup; } - - if ((sock->watch = virEventAddHandleImpl(sock->fd, - VIR_EVENT_HANDLE_READABLE | - VIR_EVENT_HANDLE_ERROR | - VIR_EVENT_HANDLE_HANGUP, - qemudDispatchServerEvent, - server, NULL)) < 0) { - VIR_ERROR0(_("Failed to add server event callback")); - goto cleanup; - } - } return 0; @@ -1033,6 +1012,25 @@ static int qemudNetworkInit(struct qemud_server *server) { return -1; } +static int qemudNetworkEnable(struct qemud_server *server) { + struct qemud_socket *sock; + + sock = server->sockets; + while (sock) { + if ((sock->watch = virEventAddHandleImpl(sock->fd, + VIR_EVENT_HANDLE_READABLE | + VIR_EVENT_HANDLE_ERROR | + VIR_EVENT_HANDLE_HANGUP, + qemudDispatchServerEvent, + server, NULL)) < 0) { + VIR_ERROR0(_("Failed to add server event callback")); + return -1; + } + + sock = sock->next; + } + return 0; +} static gnutls_session_t remoteInitializeTLSSession (void) @@ -2178,7 +2176,7 @@ static void qemudInactiveTimer(int timerid, void *data) { virEventUpdateTimeoutImpl(timerid, -1); } else { DEBUG0("Timer expired and inactive, shutting down"); - server->shutdown = 1; + server->quitEventThread = 1; } } @@ -2208,9 +2206,10 @@ static void qemudFreeClient(struct qemud_client *client) { VIR_FREE(client); } -static int qemudRunLoop(struct qemud_server *server) { +static void *qemudRunLoop(void *opaque) { + struct qemud_server *server = opaque; int timerid = -1; - int ret = -1, i; + int i; int timerActive = 0; virMutexLock(&server->lock); @@ -2220,7 +2219,7 @@ static int qemudRunLoop(struct qemud_server *server) { qemudInactiveTimer, server, NULL)) < 0) { VIR_ERROR0(_("Failed to register shutdown timeout")); - return -1; + return NULL; } if (min_workers > max_workers) @@ -2229,7 +2228,7 @@ static int qemudRunLoop(struct qemud_server *server) { server->nworkers = max_workers; if (VIR_ALLOC_N(server->workers, server->nworkers) < 0) { VIR_ERROR0(_("Failed to allocate workers")); - return -1; + return NULL; } for (i = 0 ; i < min_workers ; i++) { @@ -2238,7 +2237,7 @@ static int qemudRunLoop(struct qemud_server *server) { server->nactiveworkers++; } - for (;;) { + for (;!server->quitEventThread;) { /* A shutdown timeout is specified, so check * if any drivers have active state, if not * shutdown after timeout seconds @@ -2310,11 +2309,6 @@ static int qemudRunLoop(struct qemud_server *server) { server->nactiveworkers--; } } - - if (server->shutdown) { - ret = 0; - break; - } } cleanup: @@ -2333,9 +2327,28 @@ cleanup: VIR_FREE(server->workers); virMutexUnlock(&server->lock); - return ret; + return NULL; } + +static int qemudStartEventLoop(struct qemud_server *server) { + pthread_attr_t attr; + pthread_attr_init(&attr); + /* We want to join the eventloop, so don't detach it */ + /*pthread_attr_setdetachstate(&attr, 1);*/ + + if (pthread_create(&server->eventThread, + &attr, + qemudRunLoop, + server) != 0) + return -1; + + server->hasEventThread = 1; + + return 0; +} + + static void qemudCleanup(struct qemud_server *server) { struct qemud_socket *sock; @@ -3110,6 +3123,13 @@ int main(int argc, char **argv) { statuswrite = -1; } + /* Start the event loop in a background thread, since + * state initialization needs events to be being processed */ + if (qemudStartEventLoop(server) < 0) { + VIR_ERROR0("Event thread startup failed"); + goto error; + } + /* Start the stateful HV drivers * This is delibrately done after telling the parent process * we're ready, since it can take a long time and this will @@ -3119,9 +3139,32 @@ int main(int argc, char **argv) { goto error; } - qemudRunLoop(server); + /* Start accepting new clients from network */ + virMutexLock(&server->lock); + if (qemudNetworkEnable(server) < 0) { + VIR_ERROR0("Network event loop enablement failed"); + goto shutdown; + } + virMutexUnlock(&server->lock); + ret = 0; +shutdown: + /* In a non-0 shutdown scenario we need to tell event loop + * to quit immediately. Otherwise in normal case we just + * sit in the thread join forever. Sure this means the + * main thread doesn't do anything useful ever, but that's + * not too much of drain on resources + */ + if (ret != 0) { + virMutexLock(&server->lock); + if (server->hasEventThread) + /* This SIGQUIT triggers the shutdown process */ + kill(getpid(), SIGQUIT); + virMutexUnlock(&server->lock); + } + pthread_join(server->eventThread, NULL); + error: if (statuswrite != -1) { if (ret != 0) { diff --git a/daemon/libvirtd.h b/daemon/libvirtd.h index c0784d8..e3624c6 100644 --- a/daemon/libvirtd.h +++ b/daemon/libvirtd.h @@ -269,7 +269,9 @@ struct qemud_server { int sigread; int sigwrite; char *logDir; - unsigned int shutdown : 1; + pthread_t eventThread; + unsigned int hasEventThread :1; + unsigned int quitEventThread :1; #ifdef HAVE_AVAHI struct libvirtd_mdns *mdns; #endif diff --git a/src/driver.h b/src/driver.h index 0c8f923..d4a8b96 100644 --- a/src/driver.h +++ b/src/driver.h @@ -731,6 +731,7 @@ typedef struct _virStateDriver virStateDriver; typedef virStateDriver *virStateDriverPtr; struct _virStateDriver { + const char *name; virDrvStateInitialize initialize; virDrvStateCleanup cleanup; virDrvStateReload reload; diff --git a/src/libvirt.c b/src/libvirt.c index 9e87900..ea51490 100644 --- a/src/libvirt.c +++ b/src/libvirt.c @@ -827,8 +827,11 @@ int virStateInitialize(int privileged) { for (i = 0 ; i < virStateDriverTabCount ; i++) { if (virStateDriverTab[i]->initialize && - virStateDriverTab[i]->initialize(privileged) < 0) + virStateDriverTab[i]->initialize(privileged) < 0) { + VIR_ERROR("Initialization of %s state driver failed", + virStateDriverTab[i]->name); ret = -1; + } } return ret; } diff --git a/src/lxc/lxc_driver.c b/src/lxc/lxc_driver.c index 116f3ae..c76d6f4 100644 --- a/src/lxc/lxc_driver.c +++ b/src/lxc/lxc_driver.c @@ -1641,12 +1641,21 @@ static int lxcStartup(int privileged) * XXX remove this when valgrind is fixed */ ld = getenv("LD_PRELOAD"); - if (ld && strstr(ld, "vgpreload")) - return -1; + if (ld && strstr(ld, "vgpreload")) { + VIR_INFO0("Running under valgrind, disabling driver"); + return 0; + } - /* Check that the user is root */ + /* Check that the user is root, silently disable if not */ if (!privileged) { - return -1; + VIR_INFO0("Not running privileged, disabling driver"); + return 0; + } + + /* Check that this is a container enabled kernel */ + if (lxcContainerAvailable(0) < 0) { + VIR_INFO0("LXC support not available in this kernel, disabling driver"); + return 0; } if (VIR_ALLOC(lxc_driver) < 0) { @@ -1658,12 +1667,6 @@ static int lxcStartup(int privileged) } lxcDriverLock(lxc_driver); - /* Check that this is a container enabled kernel */ - if (lxcContainerAvailable(0) < 0) { - VIR_INFO0("LXC support not available in this kernel, disabling driver"); - goto cleanup; - } - if (virDomainObjListInit(&lxc_driver->domains) < 0) goto cleanup; @@ -2322,6 +2325,7 @@ static virDriver lxcDriver = { }; static virStateDriver lxcStateDriver = { + .name = "LXC", .initialize = lxcStartup, .cleanup = lxcShutdown, .active = lxcActive, diff --git a/src/network/bridge_driver.c b/src/network/bridge_driver.c index 2a6a7ab..d49cef6 100644 --- a/src/network/bridge_driver.c +++ b/src/network/bridge_driver.c @@ -1504,6 +1504,7 @@ static virNetworkDriver networkDriver = { }; static virStateDriver networkStateDriver = { + "Network", networkStartup, networkShutdown, networkReload, diff --git a/src/node_device/node_device_devkit.c b/src/node_device/node_device_devkit.c index a6c7941..d2ffa1d 100644 --- a/src/node_device/node_device_devkit.c +++ b/src/node_device/node_device_devkit.c @@ -431,6 +431,7 @@ static virDeviceMonitor devkitDeviceMonitor = { static virStateDriver devkitStateDriver = { + .name = "DeviceKit", .initialize = devkitDeviceMonitorStartup, .cleanup = devkitDeviceMonitorShutdown, .reload = devkitDeviceMonitorReload, diff --git a/src/node_device/node_device_hal.c b/src/node_device/node_device_hal.c index 18be5ed..fe8d116 100644 --- a/src/node_device/node_device_hal.c +++ b/src/node_device/node_device_hal.c @@ -873,6 +873,7 @@ static virDeviceMonitor halDeviceMonitor = { static virStateDriver halStateDriver = { + .name = "HAL", .initialize = halDeviceMonitorStartup, .cleanup = halDeviceMonitorShutdown, .reload = halDeviceMonitorReload, diff --git a/src/opennebula/one_driver.c b/src/opennebula/one_driver.c index beb48ce..f95d45e 100644 --- a/src/opennebula/one_driver.c +++ b/src/opennebula/one_driver.c @@ -761,6 +761,7 @@ static virDriver oneDriver = { }; static virStateDriver oneStateDriver = { + .name = "OpenNebula", .initialize = oneStartup, .cleanup = oneShutdown, .active = oneActive, diff --git a/src/qemu/qemu_driver.c b/src/qemu/qemu_driver.c index 1651071..1bb82eb 100644 --- a/src/qemu/qemu_driver.c +++ b/src/qemu/qemu_driver.c @@ -7150,6 +7150,7 @@ static virDriver qemuDriver = { static virStateDriver qemuStateDriver = { + .name = "QEMU", .initialize = qemudStartup, .cleanup = qemudShutdown, .reload = qemudReload, diff --git a/src/remote/remote_driver.c b/src/remote/remote_driver.c index bf001eb..2c0406b 100644 --- a/src/remote/remote_driver.c +++ b/src/remote/remote_driver.c @@ -8560,6 +8560,7 @@ static virDeviceMonitor dev_monitor = { #ifdef WITH_LIBVIRTD static virStateDriver state_driver = { + .name = "Remote", .initialize = remoteStartup, }; #endif diff --git a/src/secret/secret_driver.c b/src/secret/secret_driver.c index d61c24a..1d5b4f7 100644 --- a/src/secret/secret_driver.c +++ b/src/secret/secret_driver.c @@ -1074,6 +1074,7 @@ static virSecretDriver secretDriver = { }; static virStateDriver stateDriver = { + .name = "Secret", .initialize = secretDriverStartup, .cleanup = secretDriverCleanup, .reload = secretDriverReload, diff --git a/src/storage/storage_driver.c b/src/storage/storage_driver.c index 4f8949b..80e4543 100644 --- a/src/storage/storage_driver.c +++ b/src/storage/storage_driver.c @@ -1744,6 +1744,7 @@ static virStorageDriver storageDriver = { static virStateDriver stateDriver = { + .name = "Storage", .initialize = storageDriverStartup, .cleanup = storageDriverShutdown, .reload = storageDriverReload, diff --git a/src/uml/uml_driver.c b/src/uml/uml_driver.c index 4fb04d9..98c0e14 100644 --- a/src/uml/uml_driver.c +++ b/src/uml/uml_driver.c @@ -1855,6 +1855,7 @@ static virDriver umlDriver = { static virStateDriver umlStateDriver = { + .name = "UML", .initialize = umlStartup, .cleanup = umlShutdown, .reload = umlReload, diff --git a/src/xen/xen_driver.c b/src/xen/xen_driver.c index 5273a11..859f109 100644 --- a/src/xen/xen_driver.c +++ b/src/xen/xen_driver.c @@ -182,6 +182,7 @@ xenInitialize (int privileged ATTRIBUTE_UNUSED) } static virStateDriver state_driver = { + .name = "Xen", .initialize = xenInitialize, }; -- 1.6.2.5

On Fri, Oct 23, 2009 at 02:05:40PM +0100, Daniel P. Berrange wrote:
The virStateInitialize() call for starting up stateful drivers may require that the event loop is running already. This it is neccessary to start the event loop before this call. At the same time, network clients must not be processed until afte virStateInitialize has completed.
The qemudListenUnix() and remoteListenTCP() methods must therefore not register file handle watches, merely open the network sockets & listen() on them. This means clients can connected and are queued, pending completion of initialization
The qemudRunLoop() method is moved into a background thread that is started early to allow access to the event loop during driver initialization. The main process thread leader pretty much does nothing once the daemon is running, merely waits for the event loop thread to quit
I guess this calls for a extra page of documentation about the threading startup of the server, i.e. what starts when. threaded C code is really hard to follow and if you don't have the big picture (a single picture might be sufficient !) it rather hard to get things just from reading code.
* daemon/libvirtd.c, daemon/libvirtd.h: Move event loop into a background thread * src/driver.h: Add a 'name' field to state driver to allow easy identification during failures * src/libvirt.c: Log name of failed driver for virStateInit failures * src/lxc/lxc_driver.c: Don't return a failure code for lxcStartup() if LXC is not available on this host, simply disable the driver. * src/network/bridge_driver.c, src/node_device/node_device_devkit.c, src/node_device/node_device_hal.c, src/opennebula/one_driver.c, src/qemu/qemu_driver.c, src/remote/remote_driver.c, src/secret/secret_driver.c, src/storage/storage_driver.c, src/uml/uml_driver.c, src/xen/xen_driver.c: Fill in name field in virStateDriver struct --- daemon/libvirtd.c | 115 +++++++++++++++++++++++----------- daemon/libvirtd.h | 4 +- src/driver.h | 1 + src/libvirt.c | 5 +- src/lxc/lxc_driver.c | 24 ++++--- src/network/bridge_driver.c | 1 + src/node_device/node_device_devkit.c | 1 + src/node_device/node_device_hal.c | 1 + src/opennebula/one_driver.c | 1 + src/qemu/qemu_driver.c | 1 + src/remote/remote_driver.c | 1 + src/secret/secret_driver.c | 1 + src/storage/storage_driver.c | 1 + src/uml/uml_driver.c | 1 + src/xen/xen_driver.c | 1 + 15 files changed, 111 insertions(+), 48 deletions(-)
diff --git a/daemon/libvirtd.c b/daemon/libvirtd.c index b118da8..a20bbb8 100644
- for (;;) { + for (;!server->quitEventThread;) {
while (!server->quitEventThread) { is a bit more readable and less error prone :-)
/* A shutdown timeout is specified, so check * if any drivers have active state, if not * shutdown after timeout seconds @@ -2310,11 +2309,6 @@ static int qemudRunLoop(struct qemud_server *server) { server->nactiveworkers--; } } + +static int qemudStartEventLoop(struct qemud_server *server) { + pthread_attr_t attr; + pthread_attr_init(&attr); + /* We want to join the eventloop, so don't detach it */ + /*pthread_attr_setdetachstate(&attr, 1);*/
should we really keep that comment ? it's a bit confusing no ?
+ if (pthread_create(&server->eventThread, + &attr, + qemudRunLoop, + server) != 0) + return -1; + + server->hasEventThread = 1; + + return 0; +} [...]
+shutdown: + /* In a non-0 shutdown scenario we need to tell event loop + * to quit immediately. Otherwise in normal case we just + * sit in the thread join forever. Sure this means the + * main thread doesn't do anything useful ever, but that's + * not too much of drain on resources + */ + if (ret != 0) { + virMutexLock(&server->lock); + if (server->hasEventThread) + /* This SIGQUIT triggers the shutdown process */ + kill(getpid(), SIGQUIT); + virMutexUnlock(&server->lock); + } + pthread_join(server->eventThread, NULL);
humpf .... okay ... ACK, there is some cleanups merged in that patch too, 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 8e27ea7..9ed9bc3 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -181,6 +181,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 1bb82eb..b36839b 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" @@ -298,11 +299,24 @@ cleanup: return rc; } +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 @@ -311,17 +325,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; @@ -755,89 +765,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 @@ -848,7 +779,7 @@ qemudReadLogOutput(virConnectPtr conn, int fd, char *buf, size_t buflen, - qemudHandlerMonitorOutput func, + qemuLogHandleOutput func, const char *what, int timeout) { @@ -903,177 +834,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; @@ -1131,8 +905,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; } } @@ -1141,8 +915,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; } } @@ -1179,7 +953,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

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 | 160 ++++++++++++++++++------------------ src/qemu/qemu_monitor.c | 186 +++++++++++++++++++++++++++++++++++++----- src/qemu/qemu_monitor.h | 28 ++++++- src/qemu/qemu_monitor_text.c | 75 +++-------------- 5 files changed, 293 insertions(+), 167 deletions(-) diff --git a/src/qemu/qemu_conf.h b/src/qemu/qemu_conf.h index a6e68f8..4961074 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 b36839b..d25afb9 100644 --- a/src/qemu/qemu_driver.c +++ b/src/qemu/qemu_driver.c @@ -98,11 +98,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, @@ -123,6 +118,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) { @@ -299,25 +318,44 @@ cleanup: return rc; } -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; - } - if ((vm->monitorWatch = virEventAddHandle(vm->monitor, - VIR_EVENT_HANDLE_HANGUP | VIR_EVENT_HANDLE_ERROR, - qemudDispatchVMEvent, - vm, NULL)) < 0) - return -1; +/* + * 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; - return 0; + qemuDriverLockRO(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); + } } + /* * Open an existing VM's monitor, re-detect VCPU threads * and re-reserve the security labels in use @@ -327,11 +365,16 @@ qemuReconnectDomain(void *payload, const char *name ATTRIBUTE_UNUSED, void *opaq { virDomainObjPtr obj = payload; struct qemud_driver *driver = opaque; + qemuDomainObjPrivatePtr priv; virDomainObjLock(obj); - if (qemuConnectMonitor(obj, 1) < 0) + priv = obj->privateData; + + if ((priv->mon = qemuMonitorOpen(obj, 1, qemuHandleMonitorEOF)) == NULL) { + VIR_ERROR(_("Failed to connect monitor for %s\n"), obj->def->name); goto error; + } if (qemuUpdateActivePciHostdevs(driver, obj->def) < 0) { goto error; @@ -560,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; @@ -932,6 +978,7 @@ qemudWaitForMonitor(virConnectPtr conn, char buf[4096]; /* Plenty of space to get startup greeting */ int logfd; int ret; + qemuDomainObjPrivatePtr priv = vm->privateData; if ((logfd = qemudLogReadFD(conn, driver->logDir, vm->def->name, pos)) < 0) @@ -953,8 +1000,10 @@ qemudWaitForMonitor(virConnectPtr conn, return -1; } - if (qemuConnectMonitor(vm, 0) < 0) + if ((priv->mon = qemuMonitorOpen(vm, 0, qemuHandleMonitorEOF)) == NULL) { + VIR_ERROR(_("Failed to connect monitor for %s\n"), vm->def->name); return -1; + } return 0; } @@ -1946,6 +1995,7 @@ static void qemudShutdownVMDaemon(virConnectPtr conn, virDomainObjPtr vm) { int ret; int retries = 0; + qemuDomainObjPrivatePtr priv = vm->privateData; if (!virDomainIsActive(vm)) return; @@ -1958,15 +2008,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); @@ -2029,59 +2075,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... */ - qemuDriverLockRO(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) { @@ -2215,6 +2208,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

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 | 113 ++++++++++++++++++++++++++++++++++++++++++ src/qemu/qemu_monitor.c | 24 +++++++++ src/qemu/qemu_monitor.h | 20 +++++++ src/qemu/qemu_monitor_text.c | 103 ++------------------------------------ 4 files changed, 162 insertions(+), 98 deletions(-) diff --git a/src/qemu/qemu_driver.c b/src/qemu/qemu_driver.c index d25afb9..9c4783a 100644 --- a/src/qemu/qemu_driver.c +++ b/src/qemu/qemu_driver.c @@ -356,6 +356,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; +} + /* * Open an existing VM's monitor, re-detect VCPU threads * and re-reserve the security labels in use @@ -376,6 +483,9 @@ qemuReconnectDomain(void *payload, const char *name ATTRIBUTE_UNUSED, void *opaq goto error; } + qemuMonitorRegisterDiskSecretLookup(priv->mon, + findVolumeQcowPassphrase); + if (qemuUpdateActivePciHostdevs(driver, obj->def) < 0) { goto error; } @@ -1005,6 +1115,9 @@ qemudWaitForMonitor(virConnectPtr conn, return -1; } + qemuMonitorRegisterDiskSecretLookup(priv->mon, + findVolumeQcowPassphrase); + return 0; } diff --git a/src/qemu/qemu_monitor.c b/src/qemu/qemu_monitor.c index 5f7e20c..0906e33 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,18 @@ 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

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 | 316 ++++++++++++++++++++++++++++++ src/qemu/qemu_monitor.h | 148 ++++++++++++++ src/qemu/qemu_monitor_text.c | 436 +++++++++++++++++------------------------- src/qemu/qemu_monitor_text.h | 269 ++++++++++++--------------- 6 files changed, 842 insertions(+), 471 deletions(-) diff --git a/src/qemu/qemu_conf.h b/src/qemu/qemu_conf.h index 4961074..a6e68f8 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 9c4783a..47bd58a 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); /* Obtains a full write lock */ @@ -1126,6 +1131,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; @@ -1139,7 +1145,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 */ @@ -1167,6 +1173,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; @@ -1198,7 +1205,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")); @@ -1214,12 +1221,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); @@ -2073,7 +2081,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, @@ -2686,7 +2695,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")); @@ -2736,7 +2746,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; @@ -2897,7 +2908,8 @@ static int qemudDomainSetMemory(virDomainPtr dom, unsigned long newmem) { } if (virDomainIsActive(vm)) { - int r = qemuMonitorSetBalloon(vm, newmem); + qemuDomainObjPrivatePtr priv = vm->privateData; + int r = qemuMonitorSetBalloon(priv->mon, newmem); if (r < 0) goto cleanup; @@ -2951,7 +2963,8 @@ static int qemudDomainGetInfo(virDomainPtr dom, info->maxMem = vm->def->maxmem; if (virDomainIsActive(vm)) { - err = qemuMonitorGetBalloonInfo(vm, &balloon); + qemuDomainObjPrivatePtr priv = vm->privateData; + err = qemuMonitorGetBalloonInfo(priv->mon, &balloon); if (err < 0) goto cleanup; @@ -3058,8 +3071,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; } @@ -3102,15 +3116,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) @@ -3177,14 +3193,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: @@ -3192,7 +3210,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")); @@ -3708,7 +3726,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")); @@ -3755,7 +3774,8 @@ static char *qemudDomainDumpXML(virDomainPtr dom, /* Refresh current memory based on balloon info */ if (virDomainIsActive(vm)) { - err = qemuMonitorGetBalloonInfo(vm, &balloon); + qemuDomainObjPrivatePtr priv = vm->privateData; + err = qemuMonitorGetBalloonInfo(priv->mon, &balloon); if (err < 0) goto cleanup; if (err > 0) @@ -4327,10 +4347,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) { @@ -4350,6 +4371,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)) { @@ -4364,7 +4386,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, @@ -4381,6 +4403,7 @@ static int qemudDomainAttachUsbMassstorageDevice(virConnectPtr conn, virDomainObjPtr vm, virDomainDeviceDefPtr dev) { + qemuDomainObjPrivatePtr priv = vm->privateData; int i; for (i = 0 ; i < vm->def->ndisks ; i++) { @@ -4402,7 +4425,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); @@ -4417,6 +4440,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; @@ -4462,7 +4486,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; } @@ -4470,7 +4494,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) @@ -4480,7 +4504,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) @@ -4502,14 +4526,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; @@ -4524,6 +4548,7 @@ static int qemudDomainAttachHostPciDevice(virConnectPtr conn, virDomainObjPtr vm, virDomainDeviceDefPtr dev) { + qemuDomainObjPrivatePtr priv = vm->privateData; virDomainHostdevDefPtr hostdev = dev->data.hostdev; pciDevice *pci; @@ -4551,7 +4576,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, @@ -4576,6 +4601,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); @@ -4583,11 +4609,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); } @@ -4761,6 +4787,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)) { @@ -4782,7 +4809,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) @@ -4816,6 +4843,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]; @@ -4841,13 +4869,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) { @@ -4877,6 +4905,7 @@ static int qemudDomainDetachHostPciDevice(virConnectPtr conn, virDomainDeviceDefPtr dev) { virDomainHostdevDefPtr detach = NULL; + qemuDomainObjPrivatePtr priv = vm->privateData; int i, ret; pciDevice *pci; @@ -4911,7 +4940,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) @@ -5344,7 +5373,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, @@ -5541,11 +5572,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; } @@ -6281,6 +6313,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://")) { @@ -6302,16 +6335,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) { @@ -6376,6 +6410,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; @@ -6473,10 +6508,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; } @@ -6492,7 +6527,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) { @@ -6518,7 +6554,7 @@ static int doTunnelMigrate(virDomainPtr dom, cancel: if (retval != 0) - qemuMonitorMigrateCancel(vm); + qemuMonitorMigrateCancel(priv->mon); finish: dname = dname ? dname : dom->name; @@ -6679,8 +6715,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; @@ -6717,8 +6754,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 @@ -6798,13 +6836,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 0906e33..b0e0cd5 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" @@ -434,3 +435,318 @@ int qemuMonitorGetDiskSecret(qemuMonitorPtr mon, } + + +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

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: Acquire & release a lock for all monitor commands --- src/qemu/qemu_monitor.c | 222 +++++++++++++++++++++++++++++++++++++--------- 1 files changed, 178 insertions(+), 44 deletions(-) diff --git a/src/qemu/qemu_monitor.c b/src/qemu/qemu_monitor.c index b0e0cd5..33dbe1b 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; }; +static void qemuMonitorLock(qemuMonitorPtr mon) +{ + virMutexLock(&mon->lock); +} + +static 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,10 +361,10 @@ void qemuMonitorClose(qemuMonitorPtr mon) if (mon->fd != -1) close(mon->fd); + virMutexDestroy(&mon->lock); VIR_FREE(mon); } - void qemuMonitorRegisterDiskSecretLookup(qemuMonitorPtr mon, qemuMonitorDiskSecretLookup secretCB) { @@ -440,41 +458,61 @@ int qemuMonitorGetDiskSecret(qemuMonitorPtr mon, int qemuMonitorStartCPUs(qemuMonitorPtr mon, virConnectPtr conn) { + int ret; + qemuMonitorLock(mon); DEBUG("mon=%p, fd=%d", mon, mon->fd); - return qemuMonitorTextStartCPUs(mon, conn); + ret = qemuMonitorTextStartCPUs(mon, conn); + qemuMonitorUnlock(mon); + return ret; } int qemuMonitorStopCPUs(qemuMonitorPtr mon) { + int ret; + qemuMonitorLock(mon); DEBUG("mon=%p, fd=%d", mon, mon->fd); - return qemuMonitorTextStopCPUs(mon); + ret = qemuMonitorTextStopCPUs(mon); + qemuMonitorUnlock(mon); + return ret; } int qemuMonitorSystemPowerdown(qemuMonitorPtr mon) { + int ret; + qemuMonitorLock(mon); DEBUG("mon=%p, fd=%d", mon, mon->fd); - return qemuMonitorTextSystemPowerdown(mon); + ret = qemuMonitorTextSystemPowerdown(mon); + qemuMonitorUnlock(mon); + return ret; } int qemuMonitorGetCPUInfo(qemuMonitorPtr mon, int **pids) { + int ret; + qemuMonitorLock(mon); DEBUG("mon=%p, fd=%d", mon, mon->fd); - return qemuMonitorTextGetCPUInfo(mon, pids); + ret = qemuMonitorTextGetCPUInfo(mon, pids); + qemuMonitorUnlock(mon); + return ret; } int qemuMonitorGetBalloonInfo(qemuMonitorPtr mon, unsigned long *currmem) { + int ret; + qemuMonitorLock(mon); DEBUG("mon=%p, fd=%d", mon, mon->fd); - return qemuMonitorTextGetBalloonInfo(mon, currmem); + ret = qemuMonitorTextGetBalloonInfo(mon, currmem); + qemuMonitorUnlock(mon); + return ret; } @@ -486,38 +524,54 @@ int qemuMonitorGetBlockStatsInfo(qemuMonitorPtr mon, long long *wr_bytes, long long *errs) { + int ret; + qemuMonitorLock(mon); DEBUG("mon=%p, fd=%d dev=%s", mon, mon->fd, devname); - return qemuMonitorTextGetBlockStatsInfo(mon, devname, - rd_req, rd_bytes, - wr_req, wr_bytes, - errs); + ret = qemuMonitorTextGetBlockStatsInfo(mon, devname, + rd_req, rd_bytes, + wr_req, wr_bytes, + errs); + qemuMonitorUnlock(mon); + return ret; } int qemuMonitorSetVNCPassword(qemuMonitorPtr mon, const char *password) { + int ret; + qemuMonitorLock(mon); DEBUG("mon=%p, fd=%d", mon, mon->fd); - return qemuMonitorTextSetVNCPassword(mon, password); + ret = qemuMonitorTextSetVNCPassword(mon, password); + qemuMonitorUnlock(mon); + return ret; } int qemuMonitorSetBalloon(qemuMonitorPtr mon, unsigned long newmem) { + int ret; + qemuMonitorLock(mon); DEBUG("mon=%p, fd=%d newmem=%lu", mon, mon->fd, newmem); - return qemuMonitorTextSetBalloon(mon, newmem); + ret = qemuMonitorTextSetBalloon(mon, newmem); + qemuMonitorUnlock(mon); + return ret; } int qemuMonitorEjectMedia(qemuMonitorPtr mon, const char *devname) { + int ret; + qemuMonitorLock(mon); DEBUG("mon=%p, fd=%d devname=%s", mon, mon->fd, devname); - return qemuMonitorTextEjectMedia(mon, devname); + ret = qemuMonitorTextEjectMedia(mon, devname); + qemuMonitorUnlock(mon); + return ret; } @@ -525,10 +579,14 @@ int qemuMonitorChangeMedia(qemuMonitorPtr mon, const char *devname, const char *newmedia) { + int ret; + qemuMonitorLock(mon); DEBUG("mon=%p, fd=%d devname=%s newmedia=%s", mon, mon->fd, devname, newmedia); - return qemuMonitorTextChangeMedia(mon, devname, newmedia); + ret = qemuMonitorTextChangeMedia(mon, devname, newmedia); + qemuMonitorUnlock(mon); + return ret; } @@ -537,10 +595,14 @@ int qemuMonitorSaveVirtualMemory(qemuMonitorPtr mon, size_t length, const char *path) { + int ret; + qemuMonitorLock(mon); DEBUG("mon=%p, fd=%d offset=%llu length=%zu path=%s", mon, mon->fd, offset, length, path); - return qemuMonitorTextSaveVirtualMemory(mon, offset, length, path); + ret = qemuMonitorTextSaveVirtualMemory(mon, offset, length, path); + qemuMonitorUnlock(mon); + return ret; } int qemuMonitorSavePhysicalMemory(qemuMonitorPtr mon, @@ -548,19 +610,27 @@ int qemuMonitorSavePhysicalMemory(qemuMonitorPtr mon, size_t length, const char *path) { + int ret; + qemuMonitorLock(mon); DEBUG("mon=%p, fd=%d offset=%llu length=%zu path=%s", mon, mon->fd, offset, length, path); - return qemuMonitorTextSavePhysicalMemory(mon, offset, length, path); + ret = qemuMonitorTextSavePhysicalMemory(mon, offset, length, path); + qemuMonitorUnlock(mon); + return ret; } int qemuMonitorSetMigrationSpeed(qemuMonitorPtr mon, unsigned long bandwidth) { + int ret; + qemuMonitorLock(mon); DEBUG("mon=%p, fd=%d bandwidth=%lu", mon, mon->fd, bandwidth); - return qemuMonitorTextSetMigrationSpeed(mon, bandwidth); + ret = qemuMonitorTextSetMigrationSpeed(mon, bandwidth); + qemuMonitorUnlock(mon); + return ret; } int qemuMonitorGetMigrationStatus(qemuMonitorPtr mon, @@ -569,12 +639,16 @@ int qemuMonitorGetMigrationStatus(qemuMonitorPtr mon, unsigned long long *remaining, unsigned long long *total) { + int ret; + qemuMonitorLock(mon); DEBUG("mon=%p, fd=%d", mon, mon->fd); - return qemuMonitorTextGetMigrationStatus(mon, status, - transferred, - remaining, - total); + ret = qemuMonitorTextGetMigrationStatus(mon, status, + transferred, + remaining, + total); + qemuMonitorUnlock(mon); + return ret; } @@ -583,10 +657,14 @@ int qemuMonitorMigrateToHost(qemuMonitorPtr mon, const char *hostname, int port) { + int ret; + qemuMonitorLock(mon); DEBUG("mon=%p, fd=%d hostname=%s port=%d", mon, mon->fd, hostname, port); - return qemuMonitorTextMigrateToHost(mon, background, hostname, port); + ret = qemuMonitorTextMigrateToHost(mon, background, hostname, port); + qemuMonitorUnlock(mon); + return ret; } @@ -595,35 +673,51 @@ int qemuMonitorMigrateToCommand(qemuMonitorPtr mon, const char * const *argv, const char *target) { + int ret; + qemuMonitorLock(mon); DEBUG("mon=%p, fd=%d argv=%p target=%s", mon, mon->fd, argv, target); - return qemuMonitorTextMigrateToCommand(mon, background, argv, target); + ret = qemuMonitorTextMigrateToCommand(mon, background, argv, target); + qemuMonitorUnlock(mon); + return ret; } int qemuMonitorMigrateToUnix(qemuMonitorPtr mon, int background, const char *unixfile) { + int ret; + qemuMonitorLock(mon); DEBUG("mon=%p fd=%d unixfile=%s", mon, mon->fd, unixfile); - return qemuMonitorTextMigrateToUnix(mon, background, unixfile); + ret = qemuMonitorTextMigrateToUnix(mon, background, unixfile); + qemuMonitorUnlock(mon); + return ret; } int qemuMonitorMigrateCancel(qemuMonitorPtr mon) { + int ret; + qemuMonitorLock(mon); DEBUG("mon=%p fd=%d", mon, mon->fd); - return qemuMonitorTextMigrateCancel(mon); + ret = qemuMonitorTextMigrateCancel(mon); + qemuMonitorUnlock(mon); + return ret; } int qemuMonitorAddUSBDisk(qemuMonitorPtr mon, const char *path) { + int ret; + qemuMonitorLock(mon); DEBUG("mon=%p, fd=%d path=%s", mon, mon->fd, path); - return qemuMonitorTextAddUSBDisk(mon, path); + ret = qemuMonitorTextAddUSBDisk(mon, path); + qemuMonitorUnlock(mon); + return ret; } @@ -631,19 +725,27 @@ int qemuMonitorAddUSBDeviceExact(qemuMonitorPtr mon, int bus, int dev) { + int ret; + qemuMonitorLock(mon); DEBUG("mon=%p, fd=%d bus=%d dev=%d", mon, mon->fd, bus, dev); - return qemuMonitorTextAddUSBDeviceExact(mon, bus, dev); + ret = qemuMonitorTextAddUSBDeviceExact(mon, bus, dev); + qemuMonitorUnlock(mon); + return ret; } int qemuMonitorAddUSBDeviceMatch(qemuMonitorPtr mon, int vendor, int product) { + int ret; + qemuMonitorLock(mon); DEBUG("mon=%p, fd=%d vendor=%d product=%d", mon, mon->fd, vendor, product); - return qemuMonitorTextAddUSBDeviceMatch(mon, vendor, product); + ret = qemuMonitorTextAddUSBDeviceMatch(mon, vendor, product); + qemuMonitorUnlock(mon); + return ret; } @@ -656,16 +758,20 @@ int qemuMonitorAddPCIHostDevice(qemuMonitorPtr mon, unsigned *guestBus, unsigned *guestSlot) { + int ret; + qemuMonitorLock(mon); 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); + ret = qemuMonitorTextAddPCIHostDevice(mon, hostDomain, + hostBus, hostSlot, + hostFunction, + guestDomain, + guestBus, + guestSlot); + qemuMonitorUnlock(mon); + return ret; } @@ -676,11 +782,15 @@ int qemuMonitorAddPCIDisk(qemuMonitorPtr mon, unsigned *guestBus, unsigned *guestSlot) { + int ret; + qemuMonitorLock(mon); DEBUG("mon=%p, fd=%d path=%s bus=%s", mon, mon->fd, path, bus); - return qemuMonitorTextAddPCIDisk(mon, path, bus, - guestDomain, guestBus, guestSlot); + ret = qemuMonitorTextAddPCIDisk(mon, path, bus, + guestDomain, guestBus, guestSlot); + qemuMonitorUnlock(mon); + return ret; } @@ -690,10 +800,14 @@ int qemuMonitorAddPCINetwork(qemuMonitorPtr mon, unsigned *guestBus, unsigned *guestSlot) { + int ret; + qemuMonitorLock(mon); DEBUG("mon=%p, fd=%d nicstr=%s", mon, mon->fd, nicstr); - return qemuMonitorTextAddPCINetwork(mon, nicstr, guestDomain, - guestBus, guestSlot); + ret = qemuMonitorTextAddPCINetwork(mon, nicstr, guestDomain, + guestBus, guestSlot); + qemuMonitorUnlock(mon); + return ret; } @@ -702,11 +816,15 @@ int qemuMonitorRemovePCIDevice(qemuMonitorPtr mon, unsigned guestBus, unsigned guestSlot) { + int ret; + qemuMonitorLock(mon); DEBUG("mon=%p, fd=%d domain=%d bus=%d slot=%d", mon, mon->fd, guestDomain, guestBus, guestSlot); - return qemuMonitorTextRemovePCIDevice(mon, guestDomain, - guestBus, guestSlot); + ret = qemuMonitorTextRemovePCIDevice(mon, guestDomain, + guestBus, guestSlot); + qemuMonitorUnlock(mon); + return ret; } @@ -714,30 +832,42 @@ int qemuMonitorSendFileHandle(qemuMonitorPtr mon, const char *fdname, int fd) { + int ret; + qemuMonitorLock(mon); DEBUG("mon=%p, fd=%d fdname=%s fd=%d", mon, mon->fd, fdname, fd); - return qemuMonitorTextSendFileHandle(mon, fdname, fd); + ret = qemuMonitorTextSendFileHandle(mon, fdname, fd); + qemuMonitorUnlock(mon); + return ret; } int qemuMonitorCloseFileHandle(qemuMonitorPtr mon, const char *fdname) { + int ret; + qemuMonitorLock(mon); DEBUG("mon=%p, fd=%d fdname=%s", mon, mon->fd, fdname); - return qemuMonitorTextCloseFileHandle(mon, fdname); + ret = qemuMonitorTextCloseFileHandle(mon, fdname); + qemuMonitorUnlock(mon); + return ret; } int qemuMonitorAddHostNetwork(qemuMonitorPtr mon, const char *netstr) { + int ret; + qemuMonitorLock(mon); DEBUG("mon=%p, fd=%d netstr=%s", mon, mon->fd, netstr); - return qemuMonitorTextAddHostNetwork(mon, netstr); + ret = qemuMonitorTextAddHostNetwork(mon, netstr); + qemuMonitorUnlock(mon); + return ret; } @@ -745,8 +875,12 @@ int qemuMonitorRemoveHostNetwork(qemuMonitorPtr mon, int vlan, const char *netname) { + int ret; + qemuMonitorLock(mon); DEBUG("mon=%p, fd=%d netname=%s", mon, mon->fd, netname); - return qemuMonitorTextRemoveHostNetwork(mon, vlan, netname); + ret = qemuMonitorTextRemoveHostNetwork(mon, vlan, netname); + qemuMonitorUnlock(mon); + return ret; } -- 1.6.2.5

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 a6d8e07..82bec2a 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 8a8bfb0..5202034 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 6ed562d..4f2f048 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

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 --- src/conf/domain_conf.c | 3 +- src/qemu/qemu_driver.c | 60 ++++- src/qemu/qemu_monitor.c | 550 ++++++++++++++++++++++++++---------------- src/qemu/qemu_monitor.h | 46 +++-- src/qemu/qemu_monitor_text.c | 402 +++++++++++++++++-------------- src/qemu/qemu_monitor_text.h | 5 + 6 files changed, 647 insertions(+), 419 deletions(-) diff --git a/src/conf/domain_conf.c b/src/conf/domain_conf.c index 82bec2a..6f8704d 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 47bd58a..8f269d2 100644 --- a/src/qemu/qemu_driver.c +++ b/src/qemu/qemu_driver.c @@ -337,9 +337,8 @@ qemuHandleMonitorEOF(qemuMonitorPtr mon ATTRIBUTE_UNUSED, struct qemud_driver *driver = qemu_driver; virDomainEventPtr event = NULL; - qemuDriverLockRO(driver); + VIR_DEBUG("Received EOF on %p '%s'", vm, vm->def->name); virDomainObjLock(vm); - qemuDriverUnlock(driver); event = virDomainEventNewFromObj(vm, VIR_DOMAIN_EVENT_STOPPED, @@ -403,11 +402,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 || @@ -415,7 +427,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); @@ -428,18 +440,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); @@ -447,14 +459,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'; @@ -465,7 +477,16 @@ findVolumeQcowPassphrase(qemuMonitorPtr mon ATTRIBUTE_UNUSED, *secretRet = passphrase; *secretLen = size; - return 0; + ret = 0; + +cleanup: + /* + * XXX + * See earlier comment about lock + * + * virDomainObjUnlock(vm); + */ + return ret; } /* @@ -483,7 +504,10 @@ qemuReconnectDomain(void *payload, const char *name ATTRIBUTE_UNUSED, void *opaq priv = obj->privateData; - if ((priv->mon = qemuMonitorOpen(obj, 1, qemuHandleMonitorEOF)) == NULL) { + VIR_DEBUG("Reconnect monitor to %p '%s'", obj, obj->def->name); + + /* XXX check PID liveliness & EXE path */ + if ((priv->mon = qemuMonitorOpen(obj, qemuHandleMonitorEOF)) == NULL) { VIR_ERROR(_("Failed to connect monitor for %s\n"), obj->def->name); goto error; } @@ -512,7 +536,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); } /** @@ -1115,7 +1142,8 @@ qemudWaitForMonitor(virConnectPtr conn, return -1; } - if ((priv->mon = qemuMonitorOpen(vm, 0, qemuHandleMonitorEOF)) == NULL) { + VIR_DEBUG("Connect monitor to %p '%s'", vm, vm->def->name); + if ((priv->mon = qemuMonitorOpen(vm, qemuHandleMonitorEOF)) == NULL) { VIR_ERROR(_("Failed to connect monitor for %s\n"), vm->def->name); return -1; } @@ -2121,7 +2149,7 @@ static void qemudShutdownVMDaemon(virConnectPtr conn, if (!virDomainIsActive(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) @@ -6088,6 +6116,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 33dbe1b..fb89f9d 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; }; static void qemuMonitorLock(qemuMonitorPtr mon) @@ -61,134 +83,25 @@ static 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) -{ - if (strstr(output, "(qemu) ") == NULL) - return 1; /* keep reading */ - - return 0; -} -static int -qemuMonitorOpenCommon(virDomainObjPtr vm, - int monfd, - int reconnect) +static void qemuMonitorFree(qemuMonitorPtr mon, int lockDomain) { - 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; + VIR_DEBUG("mon=%p, lockDomain=%d", mon, lockDomain); + if (mon->vm) { + if (lockDomain) + virDomainObjLock(mon->vm); + if (!virDomainObjUnref(mon->vm) && lockDomain) + virDomainObjUnlock(mon->vm); } - - if (!reconnect) { - if (qemuMonitorReadOutput(vm, monfd, - buf, sizeof(buf), - qemuMonitorCheckPrompt, - "monitor", 10) <= 0) - ret = -1; - else - ret = 0; - } else { - ret = 0; - } - - 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); + } } void qemuMonitorRegisterDiskSecretLookup(qemuMonitorPtr mon, @@ -372,71 +543,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)); + mon->msg = msg; + qemuMonitorUpdateWatch(mon); - do { - ret = sendmsg(mon->fd, &msg, 0); - } while (ret < 0 && errno == EINTR); - - 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; } @@ -454,7 +587,6 @@ int qemuMonitorGetDiskSecret(qemuMonitorPtr mon, - int qemuMonitorStartCPUs(qemuMonitorPtr mon, virConnectPtr conn) { diff --git a/src/qemu/qemu_monitor.h b/src/qemu/qemu_monitor.h index 5f06155..46fd40f 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); @@ -57,21 +83,11 @@ void qemuMonitorClose(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

Import a JSON parsing / formatting code from http://mjson.sourceforge.net/ with some API changes to better cope with libvirt's needs. Then add basic implementation of the JSON monitor for QEMU driver. * src/util/json.c, src/util/json.h: Import code for simple JSON parsing & formatting * src/libvirt_private.syms: Include all json parser symbols * src/qemu/qemu_conf.c: Support for settin 'control' attribute to turn on JSON protocol mode * src/qemu/qemu_monitor.c: Conditionally call either Text or JSON monitor command serialization code as appropriate * src/qemu/qemu_monitor_json.c, src/qemu/qemu_monitor_json.h: new JSON based command serialization code --- src/Makefile.am | 3 + src/libvirt_private.syms | 10 + src/qemu/qemu_conf.c | 13 +- src/qemu/qemu_conf.h | 3 + src/qemu/qemu_monitor.c | 202 ++- src/qemu/qemu_monitor_json.c | 797 +++++++++ src/qemu/qemu_monitor_json.h | 155 ++ src/util/json.c | 3904 ++++++++++++++++++++++++++++++++++++++++++ src/util/json.h | 311 ++++ 9 files changed, 5350 insertions(+), 48 deletions(-) create mode 100644 src/qemu/qemu_monitor_json.c create mode 100644 src/qemu/qemu_monitor_json.h create mode 100644 src/util/json.c create mode 100644 src/util/json.h diff --git a/src/Makefile.am b/src/Makefile.am index 9ed9bc3..b87e615 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 \ @@ -184,6 +185,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/libvirt_private.syms b/src/libvirt_private.syms index 4f2f048..f0809f8 100644 --- a/src/libvirt_private.syms +++ b/src/libvirt_private.syms @@ -260,6 +260,16 @@ 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_find_first_label; + + # logging.h virLogMessage; virLogGetNbFilters; diff --git a/src/qemu/qemu_conf.c b/src/qemu/qemu_conf.c index 951a6c6..16bde05 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); @@ -1885,7 +1893,8 @@ int qemudBuildCommandLine(virConnectPtr conn, break; } - virBufferVSprintf(&opt, "file=%s", disk->src ? disk->src : ""); + 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"); diff --git a/src/qemu/qemu_conf.h b/src/qemu/qemu_conf.h index a6e68f8..65c6489 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 fb89f9d..875d339 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; }; static 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) { @@ -594,7 +603,10 @@ qemuMonitorStartCPUs(qemuMonitorPtr mon, qemuMonitorLock(mon); DEBUG("mon=%p, fd=%d", mon, mon->fd); - ret = qemuMonitorTextStartCPUs(mon, conn); + if (mon->json) + ret = qemuMonitorJSONStartCPUs(mon, conn); + else + ret = qemuMonitorTextStartCPUs(mon, conn); qemuMonitorUnlock(mon); return ret; } @@ -606,7 +618,10 @@ qemuMonitorStopCPUs(qemuMonitorPtr mon) { qemuMonitorLock(mon); DEBUG("mon=%p, fd=%d", mon, mon->fd); - ret = qemuMonitorTextStopCPUs(mon); + if (mon->json) + ret = qemuMonitorJSONStopCPUs(mon); + else + ret = qemuMonitorTextStopCPUs(mon); qemuMonitorUnlock(mon); return ret; } @@ -617,7 +632,10 @@ int qemuMonitorSystemPowerdown(qemuMonitorPtr mon) { qemuMonitorLock(mon); DEBUG("mon=%p, fd=%d", mon, mon->fd); - ret = qemuMonitorTextSystemPowerdown(mon); + if (mon->json) + ret = qemuMonitorJSONSystemPowerdown(mon); + else + ret = qemuMonitorTextSystemPowerdown(mon); qemuMonitorUnlock(mon); return ret; } @@ -630,7 +648,10 @@ int qemuMonitorGetCPUInfo(qemuMonitorPtr mon, qemuMonitorLock(mon); DEBUG("mon=%p, fd=%d", mon, mon->fd); - ret = qemuMonitorTextGetCPUInfo(mon, pids); + if (mon->json) + ret = qemuMonitorJSONGetCPUInfo(mon, pids); + else + ret = qemuMonitorTextGetCPUInfo(mon, pids); qemuMonitorUnlock(mon); return ret; } @@ -642,7 +663,10 @@ int qemuMonitorGetBalloonInfo(qemuMonitorPtr mon, qemuMonitorLock(mon); DEBUG("mon=%p, fd=%d", mon, mon->fd); - ret = qemuMonitorTextGetBalloonInfo(mon, currmem); + if (mon->json) + ret = qemuMonitorJSONGetBalloonInfo(mon, currmem); + else + ret = qemuMonitorTextGetBalloonInfo(mon, currmem); qemuMonitorUnlock(mon); return ret; } @@ -660,10 +684,16 @@ int qemuMonitorGetBlockStatsInfo(qemuMonitorPtr mon, qemuMonitorLock(mon); DEBUG("mon=%p, fd=%d dev=%s", mon, mon->fd, devname); - ret = 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); qemuMonitorUnlock(mon); return ret; } @@ -676,7 +706,10 @@ int qemuMonitorSetVNCPassword(qemuMonitorPtr mon, qemuMonitorLock(mon); DEBUG("mon=%p, fd=%d", mon, mon->fd); - ret = qemuMonitorTextSetVNCPassword(mon, password); + if (mon->json) + ret = qemuMonitorJSONSetVNCPassword(mon, password); + else + ret = qemuMonitorTextSetVNCPassword(mon, password); qemuMonitorUnlock(mon); return ret; } @@ -689,7 +722,10 @@ int qemuMonitorSetBalloon(qemuMonitorPtr mon, qemuMonitorLock(mon); DEBUG("mon=%p, fd=%d newmem=%lu", mon, mon->fd, newmem); - ret = qemuMonitorTextSetBalloon(mon, newmem); + if (mon->json) + ret = qemuMonitorJSONSetBalloon(mon, newmem); + else + ret = qemuMonitorTextSetBalloon(mon, newmem); qemuMonitorUnlock(mon); return ret; } @@ -701,7 +737,10 @@ int qemuMonitorEjectMedia(qemuMonitorPtr mon, qemuMonitorLock(mon); DEBUG("mon=%p, fd=%d devname=%s", mon, mon->fd, devname); - ret = qemuMonitorTextEjectMedia(mon, devname); + if (mon->json) + ret = qemuMonitorJSONEjectMedia(mon, devname); + else + ret = qemuMonitorTextEjectMedia(mon, devname); qemuMonitorUnlock(mon); return ret; } @@ -716,7 +755,10 @@ int qemuMonitorChangeMedia(qemuMonitorPtr mon, DEBUG("mon=%p, fd=%d devname=%s newmedia=%s", mon, mon->fd, devname, newmedia); - ret = qemuMonitorTextChangeMedia(mon, devname, newmedia); + if (mon->json) + ret = qemuMonitorJSONChangeMedia(mon, devname, newmedia); + else + ret = qemuMonitorTextChangeMedia(mon, devname, newmedia); qemuMonitorUnlock(mon); return ret; } @@ -732,7 +774,10 @@ int qemuMonitorSaveVirtualMemory(qemuMonitorPtr mon, DEBUG("mon=%p, fd=%d offset=%llu length=%zu path=%s", mon, mon->fd, offset, length, path); - ret = qemuMonitorTextSaveVirtualMemory(mon, offset, length, path); + if (mon->json) + ret = qemuMonitorJSONSaveVirtualMemory(mon, offset, length, path); + else + ret = qemuMonitorTextSaveVirtualMemory(mon, offset, length, path); qemuMonitorUnlock(mon); return ret; } @@ -747,7 +792,10 @@ int qemuMonitorSavePhysicalMemory(qemuMonitorPtr mon, DEBUG("mon=%p, fd=%d offset=%llu length=%zu path=%s", mon, mon->fd, offset, length, path); - ret = qemuMonitorTextSavePhysicalMemory(mon, offset, length, path); + if (mon->json) + ret = qemuMonitorJSONSavePhysicalMemory(mon, offset, length, path); + else + ret = qemuMonitorTextSavePhysicalMemory(mon, offset, length, path); qemuMonitorUnlock(mon); return ret; } @@ -760,7 +808,10 @@ int qemuMonitorSetMigrationSpeed(qemuMonitorPtr mon, qemuMonitorLock(mon); DEBUG("mon=%p, fd=%d bandwidth=%lu", mon, mon->fd, bandwidth); - ret = qemuMonitorTextSetMigrationSpeed(mon, bandwidth); + if (mon->json) + ret = qemuMonitorJSONSetMigrationSpeed(mon, bandwidth); + else + ret = qemuMonitorTextSetMigrationSpeed(mon, bandwidth); qemuMonitorUnlock(mon); return ret; } @@ -775,10 +826,16 @@ int qemuMonitorGetMigrationStatus(qemuMonitorPtr mon, qemuMonitorLock(mon); DEBUG("mon=%p, fd=%d", mon, mon->fd); - ret = qemuMonitorTextGetMigrationStatus(mon, status, - transferred, - remaining, - total); + if (mon->json) + ret = qemuMonitorJSONGetMigrationStatus(mon, status, + transferred, + remaining, + total); + else + ret = qemuMonitorTextGetMigrationStatus(mon, status, + transferred, + remaining, + total); qemuMonitorUnlock(mon); return ret; } @@ -794,7 +851,10 @@ int qemuMonitorMigrateToHost(qemuMonitorPtr mon, DEBUG("mon=%p, fd=%d hostname=%s port=%d", mon, mon->fd, hostname, port); - ret = qemuMonitorTextMigrateToHost(mon, background, hostname, port); + if (mon->json) + ret = qemuMonitorJSONMigrateToHost(mon, background, hostname, port); + else + ret = qemuMonitorTextMigrateToHost(mon, background, hostname, port); qemuMonitorUnlock(mon); return ret; } @@ -810,7 +870,10 @@ int qemuMonitorMigrateToCommand(qemuMonitorPtr mon, DEBUG("mon=%p, fd=%d argv=%p target=%s", mon, mon->fd, argv, target); - ret = qemuMonitorTextMigrateToCommand(mon, background, argv, target); + if (mon->json) + ret = qemuMonitorJSONMigrateToCommand(mon, background, argv, target); + else + ret = qemuMonitorTextMigrateToCommand(mon, background, argv, target); qemuMonitorUnlock(mon); return ret; } @@ -824,7 +887,10 @@ int qemuMonitorMigrateToUnix(qemuMonitorPtr mon, DEBUG("mon=%p fd=%d unixfile=%s", mon, mon->fd, unixfile); - ret = qemuMonitorTextMigrateToUnix(mon, background, unixfile); + if (mon->json) + ret = qemuMonitorJSONMigrateToUnix(mon, background, unixfile); + else + ret = qemuMonitorTextMigrateToUnix(mon, background, unixfile); qemuMonitorUnlock(mon); return ret; } @@ -835,7 +901,10 @@ int qemuMonitorMigrateCancel(qemuMonitorPtr mon) qemuMonitorLock(mon); DEBUG("mon=%p fd=%d", mon, mon->fd); - ret = qemuMonitorTextMigrateCancel(mon); + if (mon->json) + ret = qemuMonitorJSONMigrateCancel(mon); + else + ret = qemuMonitorTextMigrateCancel(mon); qemuMonitorUnlock(mon); return ret; } @@ -847,7 +916,10 @@ int qemuMonitorAddUSBDisk(qemuMonitorPtr mon, qemuMonitorLock(mon); DEBUG("mon=%p, fd=%d path=%s", mon, mon->fd, path); - ret = qemuMonitorTextAddUSBDisk(mon, path); + if (mon->json) + ret = qemuMonitorJSONAddUSBDisk(mon, path); + else + ret = qemuMonitorTextAddUSBDisk(mon, path); qemuMonitorUnlock(mon); return ret; } @@ -861,7 +933,10 @@ int qemuMonitorAddUSBDeviceExact(qemuMonitorPtr mon, qemuMonitorLock(mon); DEBUG("mon=%p, fd=%d bus=%d dev=%d", mon, mon->fd, bus, dev); - ret = qemuMonitorTextAddUSBDeviceExact(mon, bus, dev); + if (mon->json) + ret = qemuMonitorJSONAddUSBDeviceExact(mon, bus, dev); + else + ret = qemuMonitorTextAddUSBDeviceExact(mon, bus, dev); qemuMonitorUnlock(mon); return ret; } @@ -875,7 +950,10 @@ int qemuMonitorAddUSBDeviceMatch(qemuMonitorPtr mon, DEBUG("mon=%p, fd=%d vendor=%d product=%d", mon, mon->fd, vendor, product); - ret = qemuMonitorTextAddUSBDeviceMatch(mon, vendor, product); + if (mon->json) + ret = qemuMonitorJSONAddUSBDeviceMatch(mon, vendor, product); + else + ret = qemuMonitorTextAddUSBDeviceMatch(mon, vendor, product); qemuMonitorUnlock(mon); return ret; } @@ -896,12 +974,20 @@ int qemuMonitorAddPCIHostDevice(qemuMonitorPtr mon, mon, mon->fd, hostDomain, hostBus, hostSlot, hostFunction); - ret = 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); qemuMonitorUnlock(mon); return ret; } @@ -919,8 +1005,12 @@ int qemuMonitorAddPCIDisk(qemuMonitorPtr mon, DEBUG("mon=%p, fd=%d path=%s bus=%s", mon, mon->fd, path, bus); - ret = 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); qemuMonitorUnlock(mon); return ret; } @@ -936,8 +1026,12 @@ int qemuMonitorAddPCINetwork(qemuMonitorPtr mon, qemuMonitorLock(mon); DEBUG("mon=%p, fd=%d nicstr=%s", mon, mon->fd, nicstr); - ret = qemuMonitorTextAddPCINetwork(mon, nicstr, guestDomain, - guestBus, guestSlot); + if (mon->json) + ret = qemuMonitorJSONAddPCINetwork(mon, nicstr, guestDomain, + guestBus, guestSlot); + else + ret = qemuMonitorTextAddPCINetwork(mon, nicstr, guestDomain, + guestBus, guestSlot); qemuMonitorUnlock(mon); return ret; } @@ -953,8 +1047,12 @@ int qemuMonitorRemovePCIDevice(qemuMonitorPtr mon, DEBUG("mon=%p, fd=%d domain=%d bus=%d slot=%d", mon, mon->fd, guestDomain, guestBus, guestSlot); - ret = qemuMonitorTextRemovePCIDevice(mon, guestDomain, - guestBus, guestSlot); + if (mon->json) + ret = qemuMonitorJSONRemovePCIDevice(mon, guestDomain, + guestBus, guestSlot); + else + ret = qemuMonitorTextRemovePCIDevice(mon, guestDomain, + guestBus, guestSlot); qemuMonitorUnlock(mon); return ret; } @@ -969,7 +1067,10 @@ int qemuMonitorSendFileHandle(qemuMonitorPtr mon, DEBUG("mon=%p, fd=%d fdname=%s fd=%d", mon, mon->fd, fdname, fd); - ret = qemuMonitorTextSendFileHandle(mon, fdname, fd); + if (mon->json) + ret = qemuMonitorJSONSendFileHandle(mon, fdname, fd); + else + ret = qemuMonitorTextSendFileHandle(mon, fdname, fd); qemuMonitorUnlock(mon); return ret; } @@ -983,7 +1084,10 @@ int qemuMonitorCloseFileHandle(qemuMonitorPtr mon, DEBUG("mon=%p, fd=%d fdname=%s", mon, mon->fd, fdname); - ret = qemuMonitorTextCloseFileHandle(mon, fdname); + if (mon->json) + ret = qemuMonitorJSONCloseFileHandle(mon, fdname); + else + ret = qemuMonitorTextCloseFileHandle(mon, fdname); qemuMonitorUnlock(mon); return ret; } @@ -997,7 +1101,10 @@ int qemuMonitorAddHostNetwork(qemuMonitorPtr mon, DEBUG("mon=%p, fd=%d netstr=%s", mon, mon->fd, netstr); - ret = qemuMonitorTextAddHostNetwork(mon, netstr); + if (mon->json) + ret = qemuMonitorJSONAddHostNetwork(mon, netstr); + else + ret = qemuMonitorTextAddHostNetwork(mon, netstr); qemuMonitorUnlock(mon); return ret; } @@ -1012,7 +1119,10 @@ int qemuMonitorRemoveHostNetwork(qemuMonitorPtr mon, DEBUG("mon=%p, fd=%d netname=%s", mon, mon->fd, netname); - ret = qemuMonitorTextRemoveHostNetwork(mon, vlan, netname); + if (mon->json) + ret = qemuMonitorJSONRemoveHostNetwork(mon, vlan, netname); + else + ret = qemuMonitorTextRemoveHostNetwork(mon, vlan, netname); qemuMonitorUnlock(mon); return ret; } diff --git a/src/qemu/qemu_monitor_json.c b/src/qemu/qemu_monitor_json.c new file mode 100644 index 0000000..d23569c --- /dev/null +++ b/src/qemu/qemu_monitor_json.c @@ -0,0 +1,797 @@ +/* + * 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 "c-ctype.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 *args) +{ + json_t *obj, *tmp = NULL; + if (!(obj = json_new_object())) + goto no_memory; + + if (!(tmp = json_new_string(cmdname))) + goto no_memory; + + if (json_insert_pair_into_object(obj, "execute", tmp) != JSON_OK) + goto no_memory; + tmp = NULL; + + if (args && + json_insert_pair_into_object(obj, "arguments", args) != JSON_OK) + goto no_memory; + + return obj; + +no_memory: + virReportOOMError(NULL); + if (obj) + json_free_value(&obj); + if (tmp) + json_free_value(&tmp); + 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 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 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 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 ATTRIBUTE_UNUSED) +{ + int ret; + json_t *cmd = qemuMonitorJSONMakeCommand("change vnc 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 ATTRIBUTE_UNUSED) +{ + int ret; + json_t *cmd = qemuMonitorJSONMakeCommand("eject", 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 ATTRIBUTE_UNUSED, + const char *newmedia ATTRIBUTE_UNUSED) +{ + int ret; + json_t *cmd = qemuMonitorJSONMakeCommand("change", 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 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 qemuMonitorJSONAddUSBDeviceExact(qemuMonitorPtr mon, + int bus ATTRIBUTE_UNUSED, + int dev 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 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 */ diff --git a/src/util/json.c b/src/util/json.c new file mode 100644 index 0000000..69b4434 --- /dev/null +++ b/src/util/json.c @@ -0,0 +1,3904 @@ +/*************************************************************************** + * 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_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); + free (jsps->temp); + jsps->temp = NULL; + } + else + { + 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); + free ((jsps->temp)); + jsps->temp = NULL; + } + 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 (strcmp (cursor->text, text_label) == 0) { + *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..c093816 --- /dev/null +++ b/src/util/json.h @@ -0,0 +1,311 @@ +/*************************************************************************** + * 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); + + +/** + 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 10/23/2009 03:05 PM, Daniel P. Berrange wrote:
+ if (mon->json) + ret = qemuMonitorJSONSystemPowerdown(mon); + else + ret = qemuMonitorTextSystemPowerdown(mon);
Why not make a vtable? Paolo

On Mon, Oct 26, 2009 at 11:15:22AM +0100, Paolo Bonzini wrote:
On 10/23/2009 03:05 PM, Daniel P. Berrange wrote:
+ if (mon->json) + ret = qemuMonitorJSONSystemPowerdown(mon); + else + ret = qemuMonitorTextSystemPowerdown(mon);
Why not make a vtable?
We're not expecting to grow any more different protocol implementations, so I don't think it is a worthwhile tradeoff to introduce an extra layer of indirection into the code. 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 :|

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 | 6 ++++++ src/qemu/qemu_monitor_text.c | 33 +++++++++++++++++++++++++++++++++ src/qemu/qemu_monitor_text.h | 2 ++ 4 files changed, 43 insertions(+), 3 deletions(-) diff --git a/src/qemu/qemu_conf.c b/src/qemu/qemu_conf.c index 16bde05..11b8db9 100644 --- a/src/qemu/qemu_conf.c +++ b/src/qemu/qemu_conf.c @@ -1760,7 +1760,6 @@ int qemudBuildCommandLine(virConnectPtr conn, if (qemudBuildCommandLineChrDevStr(monitor_chr, buf, sizeof(buf)-8) < 0) goto error; #endif - ADD_ARG_LIT("-monitor"); ADD_ARG_LIT(buf); } @@ -1894,8 +1893,8 @@ int qemudBuildCommandLine(virConnectPtr conn, } if (disk->src) - virBufferVSprintf(&opt, "file=%s", disk->src ? disk->src : ""); - virBufferVSprintf(&opt, ",if=%s", bus); + 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 875d339..26f92f8 100644 --- a/src/qemu/qemu_monitor.c +++ b/src/qemu/qemu_monitor.c @@ -508,6 +508,12 @@ qemuMonitorOpen(virDomainObjPtr vm, virDomainObjRef(vm); + if (!mon->json) { + 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

* src/conf/storage_encryption_conf.c: Fix missing VIR_FREE --- src/conf/storage_encryption_conf.c | 1 + 1 files changed, 1 insertions(+), 0 deletions(-) diff --git a/src/conf/storage_encryption_conf.c b/src/conf/storage_encryption_conf.c index b97b989..f46e750 100644 --- a/src/conf/storage_encryption_conf.c +++ b/src/conf/storage_encryption_conf.c @@ -117,6 +117,7 @@ virStorageEncryptionSecretParse(virConnectPtr conn, xmlXPathContextPtr ctxt, _("missing volume encryption uuid")); goto cleanup; } + VIR_FREE(uuidstr); ctxt->node = old_node; return ret; -- 1.6.2.5

On Fri, Oct 23, 2009 at 02:05:29PM +0100, Daniel P. Berrange wrote:
This patch series rewrites the QEMU monitor handling almost completely.
The key theme here is to move from a totally synchronous way of interacting with the monitor, to a totally asynchronous way. This allows us to handle receipt & dispatch of asychronous events from QEMU. For example a notification of a disk-full error, or VM state change. In the process of doing this re-factoring I have also dropped in basic support/infrastructure for the JSON based monitor.
This raises another question, the basic legacy support will somehow need to preserver to cope with old version, right ?
The basic JSON parsing/formatting is working, but this needs to wait until QEMU community finalize their monitor syntax for the new commands & what args they accept.
So how many of those patches could be applied now, and how many need to wait until after next QEMU release (assuming the new support is then finalized) ? 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, Oct 28, 2009 at 05:03:55PM +0100, Daniel Veillard wrote:
On Fri, Oct 23, 2009 at 02:05:29PM +0100, Daniel P. Berrange wrote:
This patch series rewrites the QEMU monitor handling almost completely.
The key theme here is to move from a totally synchronous way of interacting with the monitor, to a totally asynchronous way. This allows us to handle receipt & dispatch of asychronous events from QEMU. For example a notification of a disk-full error, or VM state change. In the process of doing this re-factoring I have also dropped in basic support/infrastructure for the JSON based monitor.
This raises another question, the basic legacy support will somehow need to preserver to cope with old version, right ?
The text mode monitor will be supported indefinitely.
The basic JSON parsing/formatting is working, but this needs to wait until QEMU community finalize their monitor syntax for the new commands & what args they accept.
So how many of those patches could be applied now, and how many need to wait until after next QEMU release (assuming the new support is then finalized) ?
None of these patches depend on new QEMU stuff apart from the JSON one. 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 :|
participants (5)
-
Cole Robinson
-
Daniel P. Berrange
-
Daniel Veillard
-
Guido Günther
-
Paolo Bonzini