libvirt/src/xen/xs_internal.c
Matthias Bolte 6ed7374c5a Fix xen driver refcounting.
The commit cb51aa48a7 "Fix up connection
reference counting." changed the driver closing and virConnectPtr
unref-logic in virConnectClose().

Before this commit virConnectClose() closed all drivers of the given
virConnectPtr and virUnrefConnect()'ed it afterwards. After this
commit the driver-closing is done in virUnrefConnect() if and only if
the ref-count of the virConnectPtr dropped to zero.

This change in execution order leads to a virConnectPtr leak, at least
for connections to Xen.

The relevant call sequences:

virConnectOpen() -> xenUnifiedOpen() ...

... xenInotifyOpen() -> virConnectRef(conn)

... xenStoreOpen() -> xenStoreAddWatch() -> conn->refs++

virConnectClose() -> xenUnifiedClose() ...

... xenInotifyClose() -> virUnrefConnect(conn)

... xenStoreClose() -> xenStoreRemoveWatch() -> virUnrefConnect(conn)

Before the commit this additional virConnectRef/virUnrefConnect calls
where no problem, because virConnectClose() closed the drivers
explicitly and the additional refs added by the Xen subdrivers were
removed properly. After the commit this additional refs result in a
virConnectPtr leak (including a leak of the hypercall file handle;
that's how I noticed this problem), because now the drivers are only
close if and only if the ref-count drops to zero, but this cannot
happen anymore, because the additional refs from the Xen subdrivers
would only be removed if the drivers get closed, but that doesn't
happen because the ref-count cannot drop to zero.

The fix for this problem is simple: remove the
virConnectRef/virUnrefConnect calls from the Xen subdrivers (see
attached patch). Maybe someone could explain why the Xen Inotify and
Xen Store driver do this extra ref-counting, but none of the other Xen
subdrivers. It seems unnecessary to me and can be removed without
problems.

Signed-off-by: Chris Lalancette <clalance@redhat.com>
2009-09-22 15:12:48 +02:00

1407 lines
37 KiB
C

/*
* xs_internal.c: access to Xen Store
*
* Copyright (C) 2006, 2009 Red Hat, Inc.
*
* See COPYING.LIB for the License of this software
*
* Daniel Veillard <veillard@redhat.com>
*/
#include <config.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <stdlib.h>
#include <fcntl.h>
#include <sys/mman.h>
#include <sys/ioctl.h>
#include <stdint.h>
#include <xen/dom0_ops.h>
#include <xen/version.h>
#include <xen/xen.h>
#include <xs.h>
#include "virterror_internal.h"
#include "datatypes.h"
#include "driver.h"
#include "memory.h"
#include "event.h"
#include "logging.h"
#include "uuid.h"
#include "xen_driver.h"
#include "xs_internal.h"
#include "xen_hypervisor.h"
#define VIR_FROM_THIS VIR_FROM_XEN
#ifndef PROXY
static char *xenStoreDomainGetOSType(virDomainPtr domain);
static void xenStoreWatchEvent(int watch, int fd, int events, void *data);
static void xenStoreWatchListFree(xenStoreWatchListPtr list);
struct xenUnifiedDriver xenStoreDriver = {
xenStoreOpen, /* open */
xenStoreClose, /* close */
NULL, /* version */
NULL, /* hostname */
NULL, /* nodeGetInfo */
NULL, /* getCapabilities */
xenStoreListDomains, /* listDomains */
NULL, /* numOfDomains */
NULL, /* domainCreateXML */
NULL, /* domainSuspend */
NULL, /* domainResume */
xenStoreDomainShutdown, /* domainShutdown */
xenStoreDomainReboot, /* domainReboot */
NULL, /* domainDestroy */
xenStoreDomainGetOSType, /* domainGetOSType */
xenStoreDomainGetMaxMemory, /* domainGetMaxMemory */
NULL, /* domainSetMaxMemory */
xenStoreDomainSetMemory, /* domainSetMemory */
xenStoreGetDomainInfo, /* domainGetInfo */
NULL, /* domainSave */
NULL, /* domainRestore */
NULL, /* domainCoreDump */
NULL, /* domainSetVcpus */
NULL, /* domainPinVcpu */
NULL, /* domainGetVcpus */
NULL, /* domainGetMaxVcpus */
NULL, /* listDefinedDomains */
NULL, /* numOfDefinedDomains */
NULL, /* domainCreate */
NULL, /* domainDefineXML */
NULL, /* domainUndefine */
NULL, /* domainAttachDevice */
NULL, /* domainDetachDevice */
NULL, /* domainGetAutostart */
NULL, /* domainSetAutostart */
NULL, /* domainGetSchedulerType */
NULL, /* domainGetSchedulerParameters */
NULL, /* domainSetSchedulerParameters */
};
#endif /* ! PROXY */
#define virXenStoreError(conn, code, fmt...) \
virReportErrorHelper(NULL, VIR_FROM_XENSTORE, code, __FILE__, \
__FUNCTION__, __LINE__, fmt)
/************************************************************************
* *
* Helper internal APIs *
* *
************************************************************************/
#ifndef PROXY
/**
* virConnectDoStoreList:
* @conn: pointer to the hypervisor connection
* @path: the absolute path of the directory in the store to list
* @nb: OUT pointer to the number of items found
*
* Internal API querying the Xenstore for a list
*
* Returns a string which must be freed by the caller or NULL in case of error
*/
static char **
virConnectDoStoreList(virConnectPtr conn, const char *path,
unsigned int *nb)
{
xenUnifiedPrivatePtr priv;
if (conn == NULL)
return NULL;
priv = (xenUnifiedPrivatePtr) conn->privateData;
if (priv->xshandle == NULL || path == NULL || nb == NULL)
return (NULL);
return xs_directory (priv->xshandle, 0, path, nb);
}
#endif /* ! PROXY */
/**
* virDomainDoStoreQuery:
* @conn: pointer to the hypervisor connection
* @domid: id of the domain
* @path: the relative path of the data in the store to retrieve
*
* Internal API querying the Xenstore for a string value.
*
* Returns a string which must be freed by the caller or NULL in case of error
*/
static char *
virDomainDoStoreQuery(virConnectPtr conn, int domid, const char *path)
{
char s[256];
unsigned int len = 0;
xenUnifiedPrivatePtr priv;
if (!conn)
return NULL;
priv = (xenUnifiedPrivatePtr) conn->privateData;
if (priv->xshandle == NULL)
return (NULL);
snprintf(s, 255, "/local/domain/%d/%s", domid, path);
s[255] = 0;
return xs_read(priv->xshandle, 0, &s[0], &len);
}
#ifndef PROXY
/**
* virDomainDoStoreWrite:
* @domain: a domain object
* @path: the relative path of the data in the store to retrieve
*
* Internal API setting up a string value in the Xenstore
* Requires write access to the XenStore
*
* Returns 0 in case of success, -1 in case of failure
*/
static int
virDomainDoStoreWrite(virDomainPtr domain, const char *path,
const char *value)
{
char s[256];
xenUnifiedPrivatePtr priv;
int ret = -1;
if (!VIR_IS_CONNECTED_DOMAIN(domain))
return (-1);
priv = (xenUnifiedPrivatePtr) domain->conn->privateData;
if (priv->xshandle == NULL)
return (-1);
if (domain->conn->flags & VIR_CONNECT_RO)
return (-1);
snprintf(s, 255, "/local/domain/%d/%s", domain->id, path);
s[255] = 0;
if (xs_write(priv->xshandle, 0, &s[0], value, strlen(value)))
ret = 0;
return (ret);
}
/**
* virDomainGetVM:
* @domain: a domain object
*
* Internal API extracting a xenstore vm path.
*
* Returns the new string or NULL in case of error
*/
static char *
virDomainGetVM(virDomainPtr domain)
{
char *vm;
char query[200];
unsigned int len;
xenUnifiedPrivatePtr priv;
if (!VIR_IS_CONNECTED_DOMAIN(domain))
return (NULL);
priv = (xenUnifiedPrivatePtr) domain->conn->privateData;
if (priv->xshandle == NULL)
return (NULL);
snprintf(query, 199, "/local/domain/%d/vm", virDomainGetID(domain));
query[199] = 0;
vm = xs_read(priv->xshandle, 0, &query[0], &len);
return (vm);
}
/**
* virDomainGetVMInfo:
* @domain: a domain object
* @vm: the xenstore vm path
* @name: the value's path
*
* Internal API extracting one information the device used
* by the domain from xensttore
*
* Returns the new string or NULL in case of error
*/
static char *
virDomainGetVMInfo(virDomainPtr domain, const char *vm, const char *name)
{
char s[256];
char *ret = NULL;
unsigned int len = 0;
xenUnifiedPrivatePtr priv;
if (!VIR_IS_CONNECTED_DOMAIN(domain))
return (NULL);
priv = (xenUnifiedPrivatePtr) domain->conn->privateData;
if (priv->xshandle == NULL)
return (NULL);
snprintf(s, 255, "%s/%s", vm, name);
s[255] = 0;
ret = xs_read(priv->xshandle, 0, &s[0], &len);
return (ret);
}
#endif /* ! PROXY */
/************************************************************************
* *
* Canonical internal APIs *
* *
************************************************************************/
/**
* xenStoreOpen:
* @conn: pointer to the connection block
* @name: URL for the target, NULL for local
* @flags: combination of virDrvOpenFlag(s)
*
* Connects to the Xen hypervisor.
*
* Returns 0 or -1 in case of error.
*/
virDrvOpenStatus
xenStoreOpen(virConnectPtr conn,
virConnectAuthPtr auth ATTRIBUTE_UNUSED,
int flags ATTRIBUTE_UNUSED)
{
xenUnifiedPrivatePtr priv = (xenUnifiedPrivatePtr) conn->privateData;
#ifdef PROXY
priv->xshandle = xs_daemon_open_readonly();
#else
if (flags & VIR_CONNECT_RO)
priv->xshandle = xs_daemon_open_readonly();
else
priv->xshandle = xs_daemon_open();
#endif /* ! PROXY */
if (priv->xshandle == NULL) {
/*
* not being able to connect via the socket as an unprivileged
* user is rather normal, this should fallback to the proxy (or
* remote) mechanism.
*/
if (xenHavePrivilege()) {
virXenStoreError(NULL, VIR_ERR_NO_XEN,
"%s", _("failed to connect to Xen Store"));
}
return (-1);
}
#ifndef PROXY
/* Init activeDomainList */
if (VIR_ALLOC(priv->activeDomainList) < 0) {
virXenStoreError(NULL, VIR_ERR_INTERNAL_ERROR,
"%s", _("failed to allocate activeDomainList"));
return -1;
}
/* Init watch list before filling in domInfoList,
so we can know if it is the first time through
when the callback fires */
if ( VIR_ALLOC(priv->xsWatchList) < 0 ) {
virXenStoreError(NULL, VIR_ERR_INTERNAL_ERROR,
"%s", _("failed to allocate xsWatchList"));
return -1;
}
/* This will get called once at start */
if ( xenStoreAddWatch(conn, "@releaseDomain",
"releaseDomain", xenStoreDomainReleased, priv) < 0 )
{
virXenStoreError(NULL, VIR_ERR_INTERNAL_ERROR,
"%s", _("adding watch @releaseDomain"));
return -1;
}
/* The initial call of this will fill domInfoList */
if( xenStoreAddWatch(conn, "@introduceDomain",
"introduceDomain", xenStoreDomainIntroduced, priv) < 0 )
{
virXenStoreError(NULL, VIR_ERR_INTERNAL_ERROR,
"%s", _("adding watch @introduceDomain"));
return -1;
}
/* Add an event handle */
if ((priv->xsWatch = virEventAddHandle(xs_fileno(priv->xshandle),
VIR_EVENT_HANDLE_READABLE,
xenStoreWatchEvent,
conn,
NULL)) < 0)
DEBUG0("Failed to add event handle, disabling events\n");
#endif //PROXY
return 0;
}
/**
* xenStoreClose:
* @conn: pointer to the connection block
*
* Close the connection to the Xen hypervisor.
*
* Returns 0 in case of success or -1 in case of error.
*/
int
xenStoreClose(virConnectPtr conn)
{
xenUnifiedPrivatePtr priv;
if (conn == NULL) {
virXenStoreError(conn, VIR_ERR_INVALID_ARG, __FUNCTION__);
return(-1);
}
priv = (xenUnifiedPrivatePtr) conn->privateData;
#ifndef PROXY
if (xenStoreRemoveWatch(conn, "@introduceDomain", "introduceDomain") < 0) {
DEBUG0("Warning, could not remove @introduceDomain watch");
/* not fatal */
}
if (xenStoreRemoveWatch(conn, "@releaseDomain", "releaseDomain") < 0) {
DEBUG0("Warning, could not remove @releaseDomain watch");
/* not fatal */
}
xenStoreWatchListFree(priv->xsWatchList);
priv->xsWatchList = NULL;
xenUnifiedDomainInfoListFree(priv->activeDomainList);
priv->activeDomainList = NULL;
#endif
if (priv->xshandle == NULL)
return(-1);
#ifndef PROXY
if (priv->xsWatch != -1)
virEventRemoveHandle(priv->xsWatch);
#endif
xs_daemon_close(priv->xshandle);
priv->xshandle = NULL;
return (0);
}
#ifndef PROXY
/**
* xenStoreGetDomainInfo:
* @domain: pointer to the domain block
* @info: the place where information should be stored
*
* Do an hypervisor call to get the related set of domain information.
*
* Returns 0 in case of success, -1 in case of error.
*/
int
xenStoreGetDomainInfo(virDomainPtr domain, virDomainInfoPtr info)
{
char *tmp, **tmp2;
unsigned int nb_vcpus;
char request[200];
xenUnifiedPrivatePtr priv;
if (!VIR_IS_CONNECTED_DOMAIN(domain))
return (-1);
if ((domain == NULL) || (domain->conn == NULL) || (info == NULL)) {
virXenStoreError(domain ? domain->conn : NULL, VIR_ERR_INVALID_ARG,
__FUNCTION__);
return(-1);
}
priv = (xenUnifiedPrivatePtr) domain->conn->privateData;
if (priv->xshandle == NULL)
return(-1);
if (domain->id == -1)
return(-1);
tmp = virDomainDoStoreQuery(domain->conn, domain->id, "running");
if (tmp != NULL) {
if (tmp[0] == '1')
info->state = VIR_DOMAIN_RUNNING;
free(tmp);
} else {
info->state = VIR_DOMAIN_NOSTATE;
}
tmp = virDomainDoStoreQuery(domain->conn, domain->id, "memory/target");
if (tmp != NULL) {
info->memory = atol(tmp);
info->maxMem = atol(tmp);
free(tmp);
} else {
info->memory = 0;
info->maxMem = 0;
}
#if 0
/* doesn't seems to work */
tmp = virDomainDoStoreQuery(domain->conn, domain->id, "cpu_time");
if (tmp != NULL) {
info->cpuTime = atol(tmp);
free(tmp);
} else {
info->cpuTime = 0;
}
#endif
snprintf(request, 199, "/local/domain/%d/cpu", domain->id);
request[199] = 0;
tmp2 = virConnectDoStoreList(domain->conn, request, &nb_vcpus);
if (tmp2 != NULL) {
info->nrVirtCpu = nb_vcpus;
free(tmp2);
}
return (0);
}
/**
* xenStoreDomainSetMemory:
* @domain: pointer to the domain block
* @memory: the max memory size in kilobytes.
*
* Change the maximum amount of memory allowed in the xen store
*
* Returns 0 in case of success, -1 in case of error.
*/
int
xenStoreDomainSetMemory(virDomainPtr domain, unsigned long memory)
{
int ret;
char value[20];
if ((domain == NULL) || (domain->conn == NULL) ||
(memory < 1024 * MIN_XEN_GUEST_SIZE)) {
virXenStoreError(domain ? domain->conn : NULL, VIR_ERR_INVALID_ARG,
__FUNCTION__);
return(-1);
}
if (domain->id == -1)
return(-1);
if ((domain->id == 0) && (memory < (2 * MIN_XEN_GUEST_SIZE * 1024)))
return(-1);
snprintf(value, 19, "%lu", memory);
value[19] = 0;
ret = virDomainDoStoreWrite(domain, "memory/target", &value[0]);
if (ret < 0)
return (-1);
return (0);
}
/**
* xenStoreDomainGetMaxMemory:
* @domain: pointer to the domain block
*
* Ask the xenstore for the maximum memory allowed for a domain
*
* Returns the memory size in kilobytes or 0 in case of error.
*/
unsigned long
xenStoreDomainGetMaxMemory(virDomainPtr domain)
{
char *tmp;
unsigned long ret = 0;
xenUnifiedPrivatePtr priv;
if (!VIR_IS_CONNECTED_DOMAIN(domain))
return (ret);
if (domain->id == -1)
return(-1);
priv = domain->conn->privateData;
xenUnifiedLock(priv);
tmp = virDomainDoStoreQuery(domain->conn, domain->id, "memory/target");
if (tmp != NULL) {
ret = (unsigned long) atol(tmp);
VIR_FREE(tmp);
}
xenUnifiedUnlock(priv);
return(ret);
}
/**
* xenStoreNumOfDomains:
* @conn: pointer to the hypervisor connection
*
* Provides the number of active domains.
*
* Returns the number of domain found or -1 in case of error
*/
int
xenStoreNumOfDomains(virConnectPtr conn)
{
unsigned int num;
char **idlist;
int ret = -1;
xenUnifiedPrivatePtr priv;
if (conn == NULL) {
virXenStoreError(conn, VIR_ERR_INVALID_ARG, __FUNCTION__);
return -1;
}
priv = (xenUnifiedPrivatePtr) conn->privateData;
if (priv->xshandle == NULL) {
virXenStoreError(conn, VIR_ERR_INVALID_ARG, __FUNCTION__);
return(-1);
}
idlist = xs_directory(priv->xshandle, 0, "/local/domain", &num);
if (idlist) {
free(idlist);
ret = num;
}
return(ret);
}
/**
* xenStoreDoListDomains:
* @conn: pointer to the hypervisor connection
* @ids: array to collect the list of IDs of active domains
* @maxids: size of @ids
*
* Internal API: collect the list of active domains, and store
* their ID in @maxids. The driver lock must be held.
*
* Returns the number of domain found or -1 in case of error
*/
static int
xenStoreDoListDomains(xenUnifiedPrivatePtr priv, int *ids, int maxids)
{
char **idlist = NULL, *endptr;
unsigned int num, i;
int ret = -1;
long id;
if (priv->xshandle == NULL)
goto out;
idlist = xs_directory (priv->xshandle, 0, "/local/domain", &num);
if (idlist == NULL)
goto out;
for (ret = 0, i = 0; (i < num) && (ret < maxids); i++) {
id = strtol(idlist[i], &endptr, 10);
if ((endptr == idlist[i]) || (*endptr != 0))
goto out;
ids[ret++] = (int) id;
}
out:
VIR_FREE (idlist);
return ret;
}
/**
* xenStoreListDomains:
* @conn: pointer to the hypervisor connection
* @ids: array to collect the list of IDs of active domains
* @maxids: size of @ids
*
* Collect the list of active domains, and store their ID in @maxids
*
* Returns the number of domain found or -1 in case of error
*/
int
xenStoreListDomains(virConnectPtr conn, int *ids, int maxids)
{
xenUnifiedPrivatePtr priv;
int ret;
if ((conn == NULL) || (ids == NULL)) {
virXenStoreError(conn, VIR_ERR_INVALID_ARG, __FUNCTION__);
return(-1);
}
priv = (xenUnifiedPrivatePtr) conn->privateData;
xenUnifiedLock(priv);
ret = xenStoreDoListDomains(priv, ids, maxids);
xenUnifiedUnlock(priv);
return(ret);
}
/**
* xenStoreLookupByName:
* @conn: A xend instance
* @name: The name of the domain
*
* Try to lookup a domain on the Xen Store based on its name.
*
* Returns a new domain object or NULL in case of failure
*/
virDomainPtr
xenStoreLookupByName(virConnectPtr conn, const char *name)
{
virDomainPtr ret = NULL;
unsigned int num, i, len;
long id = -1;
char **idlist = NULL, *endptr;
char prop[200], *tmp;
int found = 0;
struct xend_domain *xenddomain = NULL;
xenUnifiedPrivatePtr priv;
if ((conn == NULL) || (name == NULL)) {
virXenStoreError(conn, VIR_ERR_INVALID_ARG, __FUNCTION__);
return(NULL);
}
priv = (xenUnifiedPrivatePtr) conn->privateData;
if (priv->xshandle == NULL)
return(NULL);
idlist = xs_directory(priv->xshandle, 0, "/local/domain", &num);
if (idlist == NULL)
goto done;
for (i = 0; i < num; i++) {
id = strtol(idlist[i], &endptr, 10);
if ((endptr == idlist[i]) || (*endptr != 0)) {
goto done;
}
#if 0
if (virConnectCheckStoreID(conn, (int) id) < 0)
continue;
#endif
snprintf(prop, 199, "/local/domain/%s/name", idlist[i]);
prop[199] = 0;
tmp = xs_read(priv->xshandle, 0, prop, &len);
if (tmp != NULL) {
found = STREQ (name, tmp);
free(tmp);
if (found)
break;
}
}
if (!found)
goto done;
ret = virGetDomain(conn, name, NULL);
if (ret == NULL)
goto done;
ret->id = id;
done:
free(xenddomain);
free(idlist);
return(ret);
}
/**
* xenStoreDomainShutdown:
* @domain: pointer to the Domain block
*
* Shutdown the domain, the OS is requested to properly shutdown
* and the domain may ignore it. It will return immediately
* after queuing the request.
*
* Returns 0 in case of success, -1 in case of error.
*/
int
xenStoreDomainShutdown(virDomainPtr domain)
{
int ret;
xenUnifiedPrivatePtr priv;
if ((domain == NULL) || (domain->conn == NULL)) {
virXenStoreError((domain ? domain->conn : NULL), VIR_ERR_INVALID_ARG,
__FUNCTION__);
return(-1);
}
if (domain->id == -1 || domain->id == 0)
return(-1);
/*
* this is very hackish, the domU kernel probes for a special
* node in the xenstore and launch the shutdown command if found.
*/
priv = domain->conn->privateData;
xenUnifiedLock(priv);
ret = virDomainDoStoreWrite(domain, "control/shutdown", "poweroff");
xenUnifiedUnlock(priv);
return ret;
}
/**
* xenStoreDomainReboot:
* @domain: pointer to the Domain block
* @flags: extra flags for the reboot operation, not used yet
*
* Reboot the domain, the OS is requested to properly shutdown
* and reboot but the domain may ignore it. It will return immediately
* after queuing the request.
*
* Returns 0 in case of success, -1 in case of error.
*/
int
xenStoreDomainReboot(virDomainPtr domain, unsigned int flags ATTRIBUTE_UNUSED)
{
int ret;
xenUnifiedPrivatePtr priv;
if ((domain == NULL) || (domain->conn == NULL)) {
virXenStoreError((domain ? domain->conn : NULL), VIR_ERR_INVALID_ARG,
__FUNCTION__);
return(-1);
}
if (domain->id == -1 || domain->id == 0)
return(-1);
/*
* this is very hackish, the domU kernel probes for a special
* node in the xenstore and launch the shutdown command if found.
*/
priv = domain->conn->privateData;
xenUnifiedLock(priv);
ret = virDomainDoStoreWrite(domain, "control/shutdown", "reboot");
xenUnifiedUnlock(priv);
return ret;
}
/*
* xenStoreDomainGetOSType:
* @domain: a domain object
*
* Get the type of domain operation system.
*
* Returns the new string or NULL in case of error, the string must be
* freed by the caller.
*/
static char *
xenStoreDomainGetOSType(virDomainPtr domain) {
char *vm, *str = NULL;
if ((domain == NULL) || (domain->conn == NULL)) {
virXenStoreError((domain ? domain->conn : NULL), VIR_ERR_INVALID_ARG,
__FUNCTION__);
return(NULL);
}
vm = virDomainGetVM(domain);
if (vm) {
xenUnifiedPrivatePtr priv = domain->conn->privateData;
xenUnifiedLock(priv);
str = virDomainGetVMInfo(domain, vm, "image/ostype");
xenUnifiedUnlock(priv);
VIR_FREE(vm);
}
return (str);
}
#endif /* ! PROXY */
/**
* xenStoreDomainGetVNCPort:
* @conn: the hypervisor connection
* @domid: id of the domain
*
* Return the port number on which the domain is listening for VNC
* connections.
*
* The caller must hold the lock on the privateData
* associated with the 'conn' parameter.
*
* Returns the port number, -1 in case of error
*/
int xenStoreDomainGetVNCPort(virConnectPtr conn, int domid) {
char *tmp;
int ret = -1;
tmp = virDomainDoStoreQuery(conn, domid, "console/vnc-port");
if (tmp != NULL) {
char *end;
ret = strtol(tmp, &end, 10);
if (ret == 0 && end == tmp)
ret = -1;
free(tmp);
}
return(ret);
}
/**
* xenStoreDomainGetConsolePath:
* @conn: the hypervisor connection
* @domid: id of the domain
*
* Return the path to the psuedo TTY on which the guest domain's
* serial console is attached.
*
* Returns the path to the serial console. It is the callers
* responsibilty to free() the return string. Returns NULL
* on error
*
* The caller must hold the lock on the privateData
* associated with the 'conn' parameter.
*/
char * xenStoreDomainGetConsolePath(virConnectPtr conn, int domid) {
return virDomainDoStoreQuery(conn, domid, "console/tty");
}
#ifdef PROXY
/*
* xenStoreDomainGetOSTypeID:
* @conn: pointer to the connection.
* @id: the domain id
*
* Get the type of domain operation system.
*
* The caller must hold the lock on the privateData
* associated with the 'conn' parameter.
*
* Returns the new string or NULL in case of error, the string must be
* freed by the caller.
*/
char *
xenStoreDomainGetOSTypeID(virConnectPtr conn, int id) {
char *vm, *str = NULL;
char query[200];
unsigned int len;
xenUnifiedPrivatePtr priv;
if (id < 0)
return(NULL);
priv = (xenUnifiedPrivatePtr) conn->privateData;
if (priv->xshandle == NULL)
return (NULL);
snprintf(query, 199, "/local/domain/%d/vm", id);
query[199] = 0;
vm = xs_read(priv->xshandle, 0, &query[0], &len);
if (vm) {
snprintf(query, 199, "%s/image/ostype", vm);
str = xs_read(priv->xshandle, 0, &query[0], &len);
free(vm);
}
if (str == NULL)
str = strdup("linux");
return (str);
}
#endif /* PROXY */
/*
* xenStoreDomainGetNetworkID:
* @conn: pointer to the connection.
* @id: the domain id
* @mac: the mac address
*
* Get the reference (i.e. the string number) for the device on that domain
* which uses the given mac address
*
* The caller must hold the lock on the privateData
* associated with the 'conn' parameter.
*
* Returns the new string or NULL in case of error, the string must be
* freed by the caller.
*/
char *
xenStoreDomainGetNetworkID(virConnectPtr conn, int id, const char *mac) {
char dir[80], path[128], **list = NULL, *val = NULL;
unsigned int len, i, num;
char *ret = NULL;
xenUnifiedPrivatePtr priv;
if (id < 0)
return(NULL);
priv = (xenUnifiedPrivatePtr) conn->privateData;
if (priv->xshandle == NULL)
return (NULL);
if (mac == NULL)
return (NULL);
snprintf(dir, sizeof(dir), "/local/domain/0/backend/vif/%d", id);
list = xs_directory(priv->xshandle, 0, dir, &num);
if (list == NULL)
return(NULL);
for (i = 0; i < num; i++) {
snprintf(path, sizeof(path), "%s/%s/%s", dir, list[i], "mac");
if ((val = xs_read(priv->xshandle, 0, path, &len)) == NULL)
break;
bool match = (virMacAddrCompare(val, mac) == 0);
VIR_FREE(val);
if (match) {
ret = strdup(list[i]);
break;
}
}
VIR_FREE(list);
return(ret);
}
/*
* xenStoreDomainGetDiskID:
* @conn: pointer to the connection.
* @id: the domain id
* @dev: the virtual block device name
*
* Get the reference (i.e. the string number) for the device on that domain
* which uses the given virtual block device name
*
* The caller must hold the lock on the privateData
* associated with the 'conn' parameter.
*
* Returns the new string or NULL in case of error, the string must be
* freed by the caller.
*/
char *
xenStoreDomainGetDiskID(virConnectPtr conn, int id, const char *dev) {
char dir[80], path[128], **list = NULL, *val = NULL;
unsigned int devlen, len, i, num;
char *ret = NULL;
xenUnifiedPrivatePtr priv;
if (id < 0)
return(NULL);
priv = (xenUnifiedPrivatePtr) conn->privateData;
if (priv->xshandle == NULL)
return (NULL);
if (dev == NULL)
return (NULL);
devlen = strlen(dev);
if (devlen <= 0)
return (NULL);
snprintf(dir, sizeof(dir), "/local/domain/0/backend/vbd/%d", id);
list = xs_directory(priv->xshandle, 0, dir, &num);
if (list != NULL) {
for (i = 0; i < num; i++) {
snprintf(path, sizeof(path), "%s/%s/%s", dir, list[i], "dev");
val = xs_read(priv->xshandle, 0, path, &len);
if (val == NULL)
break;
if ((devlen != len) || memcmp(val, dev, len)) {
free (val);
} else {
ret = strdup(list[i]);
free (val);
free (list);
return (ret);
}
}
free (list);
}
snprintf(dir, sizeof(dir), "/local/domain/0/backend/tap/%d", id);
list = xs_directory(priv->xshandle, 0, dir, &num);
if (list != NULL) {
for (i = 0; i < num; i++) {
snprintf(path, sizeof(path), "%s/%s/%s", dir, list[i], "dev");
val = xs_read(priv->xshandle, 0, path, &len);
if (val == NULL)
break;
if ((devlen != len) || memcmp(val, dev, len)) {
free (val);
} else {
ret = strdup(list[i]);
free (val);
free (list);
return (ret);
}
}
free (list);
}
return (NULL);
}
/*
* The caller must hold the lock on the privateData
* associated with the 'conn' parameter.
*/
char *xenStoreDomainGetName(virConnectPtr conn,
int id) {
char prop[200];
xenUnifiedPrivatePtr priv;
unsigned int len;
priv = (xenUnifiedPrivatePtr) conn->privateData;
if (priv->xshandle == NULL)
return(NULL);
snprintf(prop, 199, "/local/domain/%d/name", id);
prop[199] = 0;
return xs_read(priv->xshandle, 0, prop, &len);
}
#ifndef PROXY
/*
* The caller must hold the lock on the privateData
* associated with the 'conn' parameter.
*/
int xenStoreDomainGetUUID(virConnectPtr conn,
int id,
unsigned char *uuid) {
char prop[200];
xenUnifiedPrivatePtr priv;
unsigned int len;
char *uuidstr;
int ret = 0;
priv = (xenUnifiedPrivatePtr) conn->privateData;
if (priv->xshandle == NULL)
return -1;
snprintf(prop, 199, "/local/domain/%d/vm", id);
prop[199] = 0;
// This will return something like
// /vm/00000000-0000-0000-0000-000000000000
uuidstr = xs_read(priv->xshandle, 0, prop, &len);
// remove "/vm/"
ret = virUUIDParse(uuidstr + 4, uuid);
VIR_FREE(uuidstr);
return ret;
}
static void
xenStoreWatchListFree(xenStoreWatchListPtr list)
{
int i;
for (i=0; i<list->count; i++) {
VIR_FREE(list->watches[i]->path);
VIR_FREE(list->watches[i]->token);
VIR_FREE(list->watches[i]);
}
VIR_FREE(list);
}
/*
* The caller must hold the lock on the privateData
* associated with the 'conn' parameter.
*/
int xenStoreAddWatch(virConnectPtr conn,
const char *path,
const char *token,
xenStoreWatchCallback cb,
void *opaque)
{
xenStoreWatchPtr watch;
int n;
xenStoreWatchListPtr list;
xenUnifiedPrivatePtr priv = (xenUnifiedPrivatePtr) conn->privateData;
if (priv->xshandle == NULL)
return -1;
list = priv->xsWatchList;
if(!list)
return -1;
/* check if we already have this callback on our list */
for (n=0; n < list->count; n++) {
if( STREQ(list->watches[n]->path, path) &&
STREQ(list->watches[n]->token, token)) {
virXenStoreError(NULL, VIR_ERR_INTERNAL_ERROR,
"%s", _("watch already tracked"));
return -1;
}
}
if (VIR_ALLOC(watch) < 0)
return -1;
watch->path = strdup(path);
watch->token = strdup(token);
watch->cb = cb;
watch->opaque = opaque;
/* Make space on list */
n = list->count;
if (VIR_REALLOC_N(list->watches, n + 1) < 0) {
virXenStoreError(NULL, VIR_ERR_INTERNAL_ERROR,
"%s", _("reallocating list"));
VIR_FREE(watch);
return -1;
}
list->watches[n] = watch;
list->count++;
return xs_watch(priv->xshandle, watch->path, watch->token);
}
/*
* The caller must hold the lock on the privateData
* associated with the 'conn' parameter.
*/
int xenStoreRemoveWatch(virConnectPtr conn,
const char *path,
const char *token)
{
int i;
xenStoreWatchListPtr list;
xenUnifiedPrivatePtr priv = (xenUnifiedPrivatePtr) conn->privateData;
if (priv->xshandle == NULL)
return -1;
list = priv->xsWatchList;
if(!list)
return -1;
for (i = 0 ; i < list->count ; i++) {
if( STREQ(list->watches[i]->path, path) &&
STREQ(list->watches[i]->token, token)) {
if (!xs_unwatch(priv->xshandle,
list->watches[i]->path,
list->watches[i]->token))
{
DEBUG0("WARNING: Could not remove watch");
/* Not fatal, continue */
}
VIR_FREE(list->watches[i]->path);
VIR_FREE(list->watches[i]->token);
VIR_FREE(list->watches[i]);
if (i < (list->count - 1))
memmove(list->watches + i,
list->watches + i + 1,
sizeof(*(list->watches)) *
(list->count - (i + 1)));
if (VIR_REALLOC_N(list->watches,
list->count - 1) < 0) {
; /* Failure to reduce memory allocation isn't fatal */
}
list->count--;
return 0;
}
}
return -1;
}
static xenStoreWatchPtr
xenStoreFindWatch(xenStoreWatchListPtr list,
const char *path,
const char *token)
{
int i;
for (i = 0 ; i < list->count ; i++)
if( STREQ(path, list->watches[i]->path) &&
STREQ(token, list->watches[i]->token) )
return list->watches[i];
return NULL;
}
static void
xenStoreWatchEvent(int watch ATTRIBUTE_UNUSED,
int fd ATTRIBUTE_UNUSED,
int events,
void *data)
{
char **event;
char *path;
char *token;
unsigned int stringCount;
xenStoreWatchPtr sw;
virConnectPtr conn = data;
xenUnifiedPrivatePtr priv = (xenUnifiedPrivatePtr) conn->privateData;
if(!priv) return;
/* only set a watch on read and write events */
if (events & (VIR_EVENT_HANDLE_ERROR | VIR_EVENT_HANDLE_HANGUP)) return;
xenUnifiedLock(priv);
if(!priv->xshandle)
goto cleanup;
event = xs_read_watch(priv->xshandle, &stringCount);
if (!event)
goto cleanup;
path = event[XS_WATCH_PATH];
token = event[XS_WATCH_TOKEN];
sw = xenStoreFindWatch(priv->xsWatchList, path, token);
if( sw )
sw->cb(conn, path, token, sw->opaque);
VIR_FREE(event);
cleanup:
xenUnifiedUnlock(priv);
}
/*
* The domain callback for the @introduceDomain watch
*
* The lock on 'priv' is held when calling this
*/
int xenStoreDomainIntroduced(virConnectPtr conn,
const char *path ATTRIBUTE_UNUSED,
const char *token ATTRIBUTE_UNUSED,
void *opaque)
{
int i, j, found, missing = 0, retries = 20;
int new_domain_cnt;
int *new_domids;
int nread;
xenUnifiedPrivatePtr priv = opaque;
retry:
new_domain_cnt = xenStoreNumOfDomains(conn);
if( VIR_ALLOC_N(new_domids,new_domain_cnt) < 0 ) {
virReportOOMError(NULL);
return -1;
}
nread = xenStoreDoListDomains(priv, new_domids, new_domain_cnt);
if (nread != new_domain_cnt) {
// mismatch. retry this read
VIR_FREE(new_domids);
goto retry;
}
missing = 0;
for (i=0 ; i < new_domain_cnt ; i++) {
found = 0;
for (j = 0 ; j < priv->activeDomainList->count ; j++) {
if (priv->activeDomainList->doms[j]->id == new_domids[i]) {
found = 1;
break;
}
}
if (!found) {
virDomainEventPtr event;
char *name;
unsigned char uuid[VIR_UUID_BUFLEN];
if (!(name = xenStoreDomainGetName(conn, new_domids[i]))) {
missing = 1;
continue;
}
if (xenStoreDomainGetUUID(conn, new_domids[i], uuid) < 0) {
missing = 1;
VIR_FREE(name);
continue;
}
event = virDomainEventNew(new_domids[i], name, uuid,
VIR_DOMAIN_EVENT_STARTED,
VIR_DOMAIN_EVENT_STARTED_BOOTED);
if (event)
xenUnifiedDomainEventDispatch(priv, event);
/* Add to the list */
xenUnifiedAddDomainInfo(priv->activeDomainList,
new_domids[i], name, uuid);
VIR_FREE(name);
}
}
VIR_FREE(new_domids);
if (missing && retries--) {
DEBUG0("Some domains were missing, trying again");
usleep(100 * 1000);
goto retry;
}
return 0;
}
/*
* The domain callback for the @destroyDomain watch
*
* The lock on 'priv' is held when calling this
*/
int xenStoreDomainReleased(virConnectPtr conn,
const char *path ATTRIBUTE_UNUSED,
const char *token ATTRIBUTE_UNUSED,
void *opaque)
{
int i, j, found, removed, retries = 20;
int new_domain_cnt;
int *new_domids;
int nread;
xenUnifiedPrivatePtr priv = (xenUnifiedPrivatePtr) opaque;
if(!priv->activeDomainList->count) return 0;
retry:
new_domain_cnt = xenStoreNumOfDomains(conn);
if( VIR_ALLOC_N(new_domids,new_domain_cnt) < 0 ) {
virReportOOMError(NULL);
return -1;
}
nread = xenStoreDoListDomains(priv, new_domids, new_domain_cnt);
if (nread != new_domain_cnt) {
// mismatch. retry this read
VIR_FREE(new_domids);
goto retry;
}
removed = 0;
for (j=0 ; j < priv->activeDomainList->count ; j++) {
found = 0;
for (i=0 ; i < new_domain_cnt ; i++) {
if (priv->activeDomainList->doms[j]->id == new_domids[i]) {
found = 1;
break;
}
}
if (!found) {
virDomainEventPtr event =
virDomainEventNew(-1,
priv->activeDomainList->doms[j]->name,
priv->activeDomainList->doms[j]->uuid,
VIR_DOMAIN_EVENT_STOPPED,
VIR_DOMAIN_EVENT_STOPPED_SHUTDOWN);
if (event)
xenUnifiedDomainEventDispatch(priv, event);
/* Remove from the list */
xenUnifiedRemoveDomainInfo(priv->activeDomainList,
priv->activeDomainList->doms[j]->id,
priv->activeDomainList->doms[j]->name,
priv->activeDomainList->doms[j]->uuid);
removed = 1;
}
}
VIR_FREE(new_domids);
if (!removed && retries--) {
DEBUG0("No domains removed, retrying");
usleep(100 * 1000);
goto retry;
}
return 0;
}
#endif //PROXY