This patch introduces a new 'vzConn' lockable object and provides helper functions to allocate/destroy it. Now we store domain related objects such as domain list, capabitilies etc. within a single vz_driver vzConn structure, which is shared by all driver connections. It is allocated in a lazy manner in any function that is trying to acces it. When a connection to vz daemon drops, vzDestroyConnection is called, which prevents further usage of vz_driver until a new connection to it is established. Signed-off-by: Maxim Nestratov <mnestratov@xxxxxxxxxxxxx> --- src/vz/vz_driver.c | 329 ++++++++++++++++++++++++++++++++++------------------- src/vz/vz_sdk.c | 9 +- src/vz/vz_utils.c | 13 ++- src/vz/vz_utils.h | 9 +- 4 files changed, 234 insertions(+), 126 deletions(-) diff --git a/src/vz/vz_driver.c b/src/vz/vz_driver.c index 7de21d8..36030a7 100644 --- a/src/vz/vz_driver.c +++ b/src/vz/vz_driver.c @@ -63,18 +63,24 @@ VIR_LOG_INIT("parallels.parallels_driver"); #define PRLCTL "prlctl" static int vzConnectClose(virConnectPtr conn); +static virClassPtr vzConnClass; void vzDriverLock(vzConnPtr driver) { - virMutexLock(&driver->lock); + virObjectLock(driver); } void vzDriverUnlock(vzConnPtr driver) { - virMutexUnlock(&driver->lock); + virObjectUnlock(driver); } +static virMutex vz_driver_lock; +static vzConnPtr vz_driver; + +static vzConnPtr +vzConnObjNew(void); static int vzCapsAddGuestDomain(virCapsPtr caps, @@ -158,15 +164,67 @@ vzBuildCapabilities(void) goto cleanup; } +static void vzConnDispose(void * obj) +{ + vzConnPtr conn = obj; + + if (conn->server) { + prlsdkUnsubscribeFromPCSEvents(conn); + prlsdkDisconnect(conn); + } + + virObjectUnref(conn->domains); + virObjectUnref(conn->caps); + virObjectUnref(conn->xmlopt); + virObjectEventStateFree(conn->domainEventState); +} + +static int vzConnOnceInit(void) +{ + if (!(vzConnClass = virClassNew(virClassForObjectLockable(), + "vzConn", + sizeof(vzConn), + vzConnDispose))) + return -1; + + return 0; +} +VIR_ONCE_GLOBAL_INIT(vzConn) + +vzConnPtr +vzGetConnection(void) +{ + virMutexLock(&vz_driver_lock); + if (!vz_driver) + vz_driver = vzConnObjNew(); + virObjectRef(vz_driver); + virMutexUnlock(&vz_driver_lock); + return vz_driver; +} + +void +vzDestroyConnection(void) +{ + vzConnPtr privconn; + virMutexLock(&vz_driver_lock); + privconn = vz_driver; + vz_driver = NULL; + virMutexUnlock(&vz_driver_lock); + virObjectUnref(privconn); +} + static char * -vzConnectGetCapabilities(virConnectPtr conn) +vzConnectGetCapabilities(virConnectPtr conn ATTRIBUTE_UNUSED) { - vzConnPtr privconn = conn->privateData; + vzConnPtr privconn = vzGetConnection(); + if (!privconn) + return NULL; char *xml; vzDriverLock(privconn); xml = virCapabilitiesFormatXML(privconn->caps); vzDriverUnlock(privconn); + virObjectUnref(privconn); return xml; } @@ -214,78 +272,42 @@ virDomainDefParserConfig vzDomainDefParserConfig = { .domainPostParseCallback = vzDomainDefPostParse, }; - -static int -vzOpenDefault(virConnectPtr conn) +static vzConnPtr +vzConnObjNew(void) { - vzConnPtr privconn; - - if (VIR_ALLOC(privconn) < 0) - return VIR_DRV_OPEN_ERROR; - if (virMutexInit(&privconn->lock) < 0) { - virReportError(VIR_ERR_INTERNAL_ERROR, "%s", - _("cannot initialize mutex")); - goto err_free; - } - - if (prlsdkInit()) { - VIR_DEBUG("%s", _("Can't initialize Parallels SDK")); - goto err_free; - } - - if (prlsdkConnect(privconn) < 0) - goto err_free; - - if (vzInitVersion(privconn) < 0) - goto error; - - if (!(privconn->caps = vzBuildCapabilities())) - goto error; + vzConnPtr conn; - vzDomainDefParserConfig.priv = &privconn->vzCaps; - if (!(privconn->xmlopt = virDomainXMLOptionNew(&vzDomainDefParserConfig, - NULL, NULL))) - goto error; - - if (!(privconn->domains = virDomainObjListNew())) - goto error; - - if (!(privconn->domainEventState = virObjectEventStateNew())) - goto error; - - if (prlsdkSubscribeToPCSEvents(privconn)) - goto error; - - if (!(privconn->closeCallback = virNewConnectCloseCallbackData())) - goto error; - - conn->privateData = privconn; + if (vzConnInitialize() < 0) + return NULL; - if (prlsdkLoadDomains(privconn)) - goto error; + if (!(conn = virObjectLockableNew(vzConnClass))) + return NULL; - return VIR_DRV_OPEN_SUCCESS; + vzDomainDefParserConfig.priv = &conn->vzCaps; + + if (!(conn->caps = vzBuildCapabilities()) || + !(conn->xmlopt = virDomainXMLOptionNew(&vzDomainDefParserConfig, + NULL, NULL)) || + !(conn->domainEventState = virObjectEventStateNew()) || + !(conn->domains = virDomainObjListNew()) || + (vzInitVersion(conn) < 0) || + (prlsdkConnect(conn) < 0) || + (prlsdkSubscribeToPCSEvents(conn) < 0) || + (prlsdkLoadDomains(conn) < 0) + ) { + virObjectUnref(conn); + return NULL; + } - error: - virObjectUnref(privconn->closeCallback); - privconn->closeCallback = NULL; - virObjectUnref(privconn->domains); - virObjectUnref(privconn->caps); - virObjectEventStateFree(privconn->domainEventState); - prlsdkDisconnect(privconn); - prlsdkDeinit(); - err_free: - conn->privateData = NULL; - VIR_FREE(privconn); - return VIR_DRV_OPEN_ERROR; + return conn; } static virDrvOpenStatus -vzConnectOpen(virConnectPtr conn, +vzConnectOpen(virConnectPtr conn ATTRIBUTE_UNUSED, virConnectAuthPtr auth ATTRIBUTE_UNUSED, unsigned int flags) { - int ret; + vzConnPtr privconn = NULL; virCheckFlags(VIR_CONNECT_RO, VIR_DRV_OPEN_ERROR); @@ -317,44 +339,33 @@ vzConnectOpen(virConnectPtr conn, return VIR_DRV_OPEN_ERROR; } - if ((ret = vzOpenDefault(conn)) != VIR_DRV_OPEN_SUCCESS) - return ret; + if (!(privconn = vzGetConnection())) + return VIR_DRV_OPEN_ERROR; + virObjectUnref(privconn); return VIR_DRV_OPEN_SUCCESS; } static int -vzConnectClose(virConnectPtr conn) +vzConnectClose(virConnectPtr conn ATTRIBUTE_UNUSED) { - vzConnPtr privconn = conn->privateData; - + vzConnPtr privconn = vzGetConnection(); if (!privconn) return 0; - vzDriverLock(privconn); - prlsdkUnsubscribeFromPCSEvents(privconn); - virObjectUnref(privconn->caps); - virObjectUnref(privconn->xmlopt); - virObjectUnref(privconn->domains); - virObjectUnref(privconn->closeCallback); - privconn->closeCallback = NULL; - virObjectEventStateFree(privconn->domainEventState); - prlsdkDisconnect(privconn); - conn->privateData = NULL; - prlsdkDeinit(); - vzDriverUnlock(privconn); - virMutexDestroy(&privconn->lock); - - VIR_FREE(privconn); + virObjectUnref(privconn); return 0; } static int -vzConnectGetVersion(virConnectPtr conn, unsigned long *hvVer) +vzConnectGetVersion(virConnectPtr conn ATTRIBUTE_UNUSED, unsigned long *hvVer) { - vzConnPtr privconn = conn->privateData; + vzConnPtr privconn = vzGetConnection(); + if (!privconn) + return -1; *hvVer = privconn->vzVersion; + virObjectUnref(privconn); return 0; } @@ -366,38 +377,48 @@ static char *vzConnectGetHostname(virConnectPtr conn ATTRIBUTE_UNUSED) static int -vzConnectListDomains(virConnectPtr conn, int *ids, int maxids) +vzConnectListDomains(virConnectPtr conn ATTRIBUTE_UNUSED, int *ids, int maxids) { - vzConnPtr privconn = conn->privateData; int n; + vzConnPtr privconn = vzGetConnection(); + if (!privconn) + return -1; vzDriverLock(privconn); n = virDomainObjListGetActiveIDs(privconn->domains, ids, maxids, NULL, NULL); vzDriverUnlock(privconn); + virObjectUnref(privconn); return n; } static int -vzConnectNumOfDomains(virConnectPtr conn) +vzConnectNumOfDomains(virConnectPtr conn ATTRIBUTE_UNUSED) { - vzConnPtr privconn = conn->privateData; int count; + vzConnPtr privconn = vzGetConnection(); + if (!privconn) + return -1; vzDriverLock(privconn); count = virDomainObjListNumOfDomains(privconn->domains, true, NULL, NULL); vzDriverUnlock(privconn); + virObjectUnref(privconn); return count; } static int -vzConnectListDefinedDomains(virConnectPtr conn, char **const names, int maxnames) +vzConnectListDefinedDomains(virConnectPtr conn ATTRIBUTE_UNUSED, + char **const names, + int maxnames) { - vzConnPtr privconn = conn->privateData; int n; + vzConnPtr privconn = vzGetConnection(); + if (!privconn) + return -1; vzDriverLock(privconn); memset(names, 0, sizeof(*names) * maxnames); @@ -405,20 +426,23 @@ vzConnectListDefinedDomains(virConnectPtr conn, char **const names, int maxnames maxnames, NULL, NULL); vzDriverUnlock(privconn); + virObjectUnref(privconn); return n; } static int -vzConnectNumOfDefinedDomains(virConnectPtr conn) +vzConnectNumOfDefinedDomains(virConnectPtr conn ATTRIBUTE_UNUSED) { - vzConnPtr privconn = conn->privateData; int count; + vzConnPtr privconn = vzGetConnection(); + if (!privconn) + return -1; vzDriverLock(privconn); count = virDomainObjListNumOfDomains(privconn->domains, false, NULL, NULL); vzDriverUnlock(privconn); - + virObjectUnref(privconn); return count; } @@ -427,8 +451,10 @@ vzConnectListAllDomains(virConnectPtr conn, virDomainPtr **domains, unsigned int flags) { - vzConnPtr privconn = conn->privateData; int ret = -1; + vzConnPtr privconn = vzGetConnection(); + if (!privconn) + return -1; virCheckFlags(VIR_CONNECT_LIST_DOMAINS_FILTERS_ALL, -1); vzDriverLock(privconn); @@ -436,17 +462,21 @@ vzConnectListAllDomains(virConnectPtr conn, NULL, flags); vzDriverUnlock(privconn); + virObjectUnref(privconn); return ret; } static virDomainPtr vzDomainLookupByID(virConnectPtr conn, int id) { - vzConnPtr privconn = conn->privateData; virDomainPtr ret = NULL; virDomainObjPtr dom; vzDriverLock(privconn); + vzConnPtr privconn = vzGetConnection(); + if (!privconn) + return NULL; + dom = virDomainObjListFindByID(privconn->domains, id); vzDriverUnlock(privconn); @@ -462,17 +492,21 @@ vzDomainLookupByID(virConnectPtr conn, int id) cleanup: if (dom) virObjectUnlock(dom); + virObjectUnref(privconn); return ret; } static virDomainPtr vzDomainLookupByUUID(virConnectPtr conn, const unsigned char *uuid) { - vzConnPtr privconn = conn->privateData; virDomainPtr ret = NULL; virDomainObjPtr dom; vzDriverLock(privconn); + vzConnPtr privconn = vzGetConnection(); + if (!privconn) + return NULL; + dom = virDomainObjListFindByUUID(privconn->domains, uuid); vzDriverUnlock(privconn); @@ -491,13 +525,16 @@ vzDomainLookupByUUID(virConnectPtr conn, const unsigned char *uuid) cleanup: if (dom) virObjectUnlock(dom); + virObjectUnref(privconn); return ret; } static virDomainPtr vzDomainLookupByName(virConnectPtr conn, const char *name) { - vzConnPtr privconn = conn->privateData; + vzConnPtr privconn = vzGetConnection(); + if (!privconn) + return NULL; virDomainPtr ret = NULL; virDomainObjPtr dom; @@ -517,6 +554,7 @@ vzDomainLookupByName(virConnectPtr conn, const char *name) cleanup: virDomainObjEndAPI(&dom); + virObjectUnref(privconn); return ret; } @@ -613,10 +651,12 @@ vzDomainGetState(virDomainPtr domain, static char * vzDomainGetXMLDesc(virDomainPtr domain, unsigned int flags) { - vzConnPtr privconn = domain->conn->privateData; virDomainDefPtr def; virDomainObjPtr privdom; char *ret = NULL; + vzConnPtr privconn = vzGetConnection(); + if (!privconn) + return NULL; /* Flags checked by virDomainDefFormat */ @@ -631,6 +671,7 @@ vzDomainGetXMLDesc(virDomainPtr domain, unsigned int flags) cleanup: if (privdom) virObjectUnlock(privdom); + virObjectUnref(privconn); return ret; } @@ -655,13 +696,16 @@ vzDomainGetAutostart(virDomainPtr domain, int *autostart) static virDomainPtr vzDomainDefineXMLFlags(virConnectPtr conn, const char *xml, unsigned int flags) { - vzConnPtr privconn = conn->privateData; virDomainPtr retdom = NULL; virDomainDefPtr def; virDomainObjPtr olddom = NULL; virDomainObjPtr newdom = NULL; unsigned int parse_flags = VIR_DOMAIN_DEF_PARSE_INACTIVE; + vzConnPtr privconn = vzGetConnection(); + if (!privconn) + return NULL; + virCheckFlags(VIR_DOMAIN_DEFINE_VALIDATE, NULL); if (flags & VIR_DOMAIN_DEFINE_VALIDATE) @@ -740,6 +784,7 @@ vzDomainDefineXMLFlags(virConnectPtr conn, const char *xml, unsigned int flags) } virDomainDefFree(def); vzDriverUnlock(privconn); + virObjectUnref(privconn); return retdom; } @@ -845,7 +890,7 @@ vzNodeGetCPUMap(virConnectPtr conn ATTRIBUTE_UNUSED, } static int -vzConnectDomainEventRegisterAny(virConnectPtr conn, +vzConnectDomainEventRegisterAny(virConnectPtr conn ATTRIBUTE_UNUSED, virDomainPtr domain, int eventID, virConnectDomainEventGenericCallback callback, @@ -853,21 +898,27 @@ vzConnectDomainEventRegisterAny(virConnectPtr conn, virFreeCallback freecb) { int ret = -1; - vzConnPtr privconn = conn->privateData; + vzConnPtr privconn = vzGetConnection(); + if (!privconn) + return -1; if (virDomainEventStateRegisterID(conn, privconn->domainEventState, domain, eventID, callback, opaque, freecb, &ret) < 0) ret = -1; + + virObjectUnref(privconn); return ret; } static int -vzConnectDomainEventDeregisterAny(virConnectPtr conn, +vzConnectDomainEventDeregisterAny(virConnectPtr conn ATTRIBUTE_UNUSED, int callbackID) { - vzConnPtr privconn = conn->privateData; int ret = -1; + vzConnPtr privconn = vzGetConnection(); + if (!privconn) + return -1; if (virObjectEventStateDeregisterID(conn, privconn->domainEventState, @@ -877,6 +928,7 @@ vzConnectDomainEventDeregisterAny(virConnectPtr conn, ret = 0; cleanup: + virObjectUnref(privconn); return ret; } @@ -939,20 +991,24 @@ static int vzDomainUndefineFlags(virDomainPtr domain, unsigned int flags) { - vzConnPtr privconn = domain->conn->privateData; virDomainObjPtr dom = NULL; - int ret; + int ret = -1; + vzConnPtr privconn = vzGetConnection(); + if (!privconn) + return -1; virCheckFlags(VIR_DOMAIN_UNDEFINE_MANAGED_SAVE | VIR_DOMAIN_UNDEFINE_SNAPSHOTS_METADATA, -1); if (!(dom = vzDomObjFromDomain(domain))) - return -1; + goto cleanup; ret = prlsdkUnregisterDomain(privconn, dom, flags); if (ret) virObjectUnlock(dom); + cleanup: + virObjectUnref(privconn); return ret; } @@ -985,16 +1041,19 @@ vzDomainHasManagedSaveImage(virDomainPtr domain, unsigned int flags) static int vzDomainManagedSave(virDomainPtr domain, unsigned int flags) { - vzConnPtr privconn = domain->conn->privateData; virDomainObjPtr dom = NULL; int state, reason; int ret = -1; + vzConnPtr privconn = vzGetConnection(); + if (!privconn) + return -1; + virCheckFlags(VIR_DOMAIN_SAVE_RUNNING | VIR_DOMAIN_SAVE_PAUSED, -1); if (!(dom = vzDomObjFromDomain(domain))) - return -1; + goto cleanup; state = virDomainObjGetState(dom, &reason); @@ -1007,7 +1066,9 @@ vzDomainManagedSave(virDomainPtr domain, unsigned int flags) ret = prlsdkDomainChangeStateLocked(privconn, dom, prlsdkSuspend); cleanup: - virObjectUnlock(dom); + if (dom) + virObjectUnlock(dom); + virObjectUnref(privconn); return ret; } @@ -1039,16 +1100,19 @@ static int vzDomainAttachDeviceFlags(virDomainPtr dom, const char *xml, unsigned int flags) { int ret = -1; - vzConnPtr privconn = dom->conn->privateData; virDomainDeviceDefPtr dev = NULL; virDomainObjPtr privdom = NULL; bool domactive = false; + vzConnPtr privconn = vzGetConnection(); + if (!privconn) + return -1; + virCheckFlags(VIR_DOMAIN_AFFECT_LIVE | VIR_DOMAIN_AFFECT_CONFIG, -1); if (!(privdom = vzDomObjFromDomain(dom))) - return -1; + goto cleanup; if (!(flags & VIR_DOMAIN_AFFECT_CONFIG)) { virReportError(VIR_ERR_OPERATION_INVALID, "%s", @@ -1100,8 +1164,11 @@ static int vzDomainAttachDeviceFlags(virDomainPtr dom, const char *xml, } ret = 0; + cleanup: - virObjectUnlock(privdom); + if (privdom) + virObjectUnlock(privdom); + virObjectUnref(privconn); return ret; } @@ -1115,17 +1182,19 @@ static int vzDomainDetachDeviceFlags(virDomainPtr dom, const char *xml, unsigned int flags) { int ret = -1; - vzConnPtr privconn = dom->conn->privateData; virDomainDeviceDefPtr dev = NULL; virDomainObjPtr privdom = NULL; bool domactive = false; + vzConnPtr privconn = vzGetConnection(); + if (!privconn) + return -1; virCheckFlags(VIR_DOMAIN_AFFECT_LIVE | VIR_DOMAIN_AFFECT_CONFIG, -1); privdom = vzDomObjFromDomain(dom); if (privdom == NULL) - return -1; + goto cleanup; if (!(flags & VIR_DOMAIN_AFFECT_CONFIG)) { virReportError(VIR_ERR_OPERATION_INVALID, "%s", @@ -1178,7 +1247,9 @@ static int vzDomainDetachDeviceFlags(virDomainPtr dom, const char *xml, ret = 0; cleanup: - virObjectUnlock(privdom); + if (privdom) + virObjectUnlock(privdom); + virObjectUnref(privconn); return ret; } @@ -1564,6 +1635,10 @@ static virConnectDriver vzConnectDriver = { static int vzStateCleanup(void) { + prlsdkDeinit(); + virObjectUnref(vz_driver); + vz_driver = NULL; + virMutexDestroy(&vz_driver_lock); return 0; } @@ -1572,7 +1647,25 @@ vzStateInitialize(bool privileged ATTRIBUTE_UNUSED, virStateInhibitCallback callback ATTRIBUTE_UNUSED, void *opaque ATTRIBUTE_UNUSED) { + if (!privileged) { + VIR_INFO("Not running privileged, disabling driver"); + return 0; + } + + if (prlsdkInit() < 0) { + VIR_DEBUG("%s", _("Can't initialize Parallels SDK")); + return -1; + } + + if (virMutexInit(&vz_driver_lock) < 0) + goto error; + + vz_driver = vzConnObjNew(); return 0; + + error: + vzStateCleanup(); + return -1; } static virStateDriver vzStateDriver = { diff --git a/src/vz/vz_sdk.c b/src/vz/vz_sdk.c index 541060a..e6f6129 100644 --- a/src/vz/vz_sdk.c +++ b/src/vz/vz_sdk.c @@ -1958,6 +1958,7 @@ prlsdkEventsHandler(PRL_HANDLE prlEvent, PRL_VOID_PTR opaque) case PET_DSP_EVT_DISP_CONNECTION_CLOSED: virConnectCloseCallbackDataCall(privconn->closeCallback, VIR_CONNECT_CLOSE_REASON_EOF); + vzDestroyConnection(); break; default: VIR_DEBUG("Skipping event of type %d", prlEventType); @@ -2084,15 +2085,19 @@ int prlsdkDomainChangeState(virDomainPtr domain, prlsdkChangeStateFunc chstate) { - vzConnPtr privconn = domain->conn->privateData; + vzConnPtr privconn = vzGetConnection(); + if (!privconn) + return -1; virDomainObjPtr dom; int ret = -1; if (!(dom = vzDomObjFromDomain(domain))) - return -1; + goto cleanup; ret = prlsdkDomainChangeStateLocked(privconn, dom, chstate); virObjectUnlock(dom); + cleanup: + virObjectUnref(privconn); return ret; } diff --git a/src/vz/vz_utils.c b/src/vz/vz_utils.c index 64e469c..f9bcbd2 100644 --- a/src/vz/vz_utils.c +++ b/src/vz/vz_utils.c @@ -71,8 +71,10 @@ virDomainObjPtr vzDomObjFromDomain(virDomainPtr domain) { virDomainObjPtr vm; - vzConnPtr privconn = domain->conn->privateData; char uuidstr[VIR_UUID_STRING_BUFLEN]; + vzConnPtr privconn = vzGetConnection(); + if (!privconn) + return NULL; vm = virDomainObjListFindByUUID(privconn->domains, domain->uuid); if (!vm) { @@ -80,11 +82,10 @@ vzDomObjFromDomain(virDomainPtr domain) virReportError(VIR_ERR_NO_DOMAIN, _("no domain with matching uuid '%s' (%s)"), uuidstr, domain->name); - return NULL; } + virObjectUnref(privconn); return vm; - } /** @@ -101,8 +102,10 @@ virDomainObjPtr vzDomObjFromDomainRef(virDomainPtr domain) { virDomainObjPtr vm; - vzConnPtr privconn = domain->conn->privateData; char uuidstr[VIR_UUID_STRING_BUFLEN]; + vzConnPtr privconn = vzGetConnection(); + if (!privconn) + return NULL; vm = virDomainObjListFindByUUIDRef(privconn->domains, domain->uuid); if (!vm) { @@ -110,9 +113,9 @@ vzDomObjFromDomainRef(virDomainPtr domain) virReportError(VIR_ERR_NO_DOMAIN, _("no domain with matching uuid '%s' (%s)"), uuidstr, domain->name); - return NULL; } + virObjectUnref(privconn); return vm; } diff --git a/src/vz/vz_utils.h b/src/vz/vz_utils.h index b415b0f..00e795f 100644 --- a/src/vz/vz_utils.h +++ b/src/vz/vz_utils.h @@ -61,7 +61,7 @@ typedef struct _vzCapabilities vzCapabilities; typedef struct _vzCapabilities *vzCapabilitiesPtr; struct _vzConn { - virMutex lock; + virObjectLockable parent; /* Immutable pointer, self-locking APIs */ virDomainObjListPtr domains; @@ -105,6 +105,13 @@ char * vzGetOutput(const char *binary, ...) ATTRIBUTE_NONNULL(1) ATTRIBUTE_SENTINEL; void vzDriverLock(vzConnPtr driver); void vzDriverUnlock(vzConnPtr driver); + +vzConnPtr +vzGetConnection(void); + +void +vzDestroyConnection(void); + virDomainObjPtr vzNewDomain(vzConnPtr privconn, char *name, -- 2.4.3 -- libvir-list mailing list libvir-list@xxxxxxxxxx https://www.redhat.com/mailman/listinfo/libvir-list