mirror of
https://gitlab.com/libvirt/libvirt.git
synced 2025-01-03 03:25:20 +00:00
Support domain lifecycle events for Xen (Ben Guthro & Daniel Berrange)
This commit is contained in:
parent
b2dbfefcf3
commit
1eeceaa649
21
ChangeLog
21
ChangeLog
@ -1,3 +1,24 @@
|
|||||||
|
Tue Nov 25 10:43:40 GMT 2008 Daniel P. Berrange <berrange@redhat.com>
|
||||||
|
|
||||||
|
Xen domain lifecycle events (Ben Guthro & Daniel Berrange)
|
||||||
|
* configure.in: Probe for inotify availability
|
||||||
|
* examples/domain-events/events-c/event-test.c: Default to NULL
|
||||||
|
connection URI to allow probing
|
||||||
|
* include/libvirt/virterror.h, src/virterror.c: Add error code
|
||||||
|
for xen inotify driver
|
||||||
|
* po/POTFILES.in: Add xen_inotify.c
|
||||||
|
* python/libvir.c: Add bogus const cast for python2.4 brokeness
|
||||||
|
* src/Makefile.am: Add xen_inotify.c
|
||||||
|
* src/util.c: Fix file descriptor leak in virRun
|
||||||
|
* src/xen_inotify.c, src/xen_inotify.h: Monitor /etc/xen and
|
||||||
|
/var/lib/xen/domains for new/old configs
|
||||||
|
* src/xen_unified.c, src/xen_unified.h: Wire in inotify driver
|
||||||
|
for events
|
||||||
|
* src/xm_internal.c, src/xm_internal.h: Refactor to allow inotify
|
||||||
|
monitoring of config files
|
||||||
|
* src/xs_internal.c, src/xs_internal.h: Use xenstore watch to
|
||||||
|
check for domain start/destroy events
|
||||||
|
|
||||||
Tue Nov 25 10:36:40 GMT 2008 Daniel P. Berrange <berrange@redhat.com>
|
Tue Nov 25 10:36:40 GMT 2008 Daniel P. Berrange <berrange@redhat.com>
|
||||||
|
|
||||||
* src/node_device_conf.c, src/node_device_conf.h,
|
* src/node_device_conf.c, src/node_device_conf.h,
|
||||||
|
16
configure.in
16
configure.in
@ -147,6 +147,8 @@ fi
|
|||||||
dnl Allow to build without Xen, QEMU/KVM, test or remote driver
|
dnl Allow to build without Xen, QEMU/KVM, test or remote driver
|
||||||
AC_ARG_WITH([xen],
|
AC_ARG_WITH([xen],
|
||||||
[ --with-xen add XEN support (on)],[],[with_xen=yes])
|
[ --with-xen add XEN support (on)],[],[with_xen=yes])
|
||||||
|
AC_ARG_WITH([xen-inotify],
|
||||||
|
[ --with-xen-inotify add XEN inotify support (on)],[],[with_xen_inotify=yes])
|
||||||
AC_ARG_WITH([qemu],
|
AC_ARG_WITH([qemu],
|
||||||
[ --with-qemu add QEMU/KVM support (on)],[],[with_qemu=yes])
|
[ --with-qemu add QEMU/KVM support (on)],[],[with_qemu=yes])
|
||||||
AC_ARG_WITH([uml],
|
AC_ARG_WITH([uml],
|
||||||
@ -334,6 +336,20 @@ AM_CONDITIONAL([WITH_XEN], [test "$with_xen" = "yes"])
|
|||||||
AC_SUBST([XEN_CFLAGS])
|
AC_SUBST([XEN_CFLAGS])
|
||||||
AC_SUBST([XEN_LIBS])
|
AC_SUBST([XEN_LIBS])
|
||||||
|
|
||||||
|
dnl
|
||||||
|
dnl check for kernel headers required by xen_inotify
|
||||||
|
dnl
|
||||||
|
if test "$with_xen" != "yes"; then
|
||||||
|
with_xen_inotify=no
|
||||||
|
fi
|
||||||
|
if test "$with_xen_inotify" != "no"; then
|
||||||
|
AC_CHECK_HEADER([linux/inotify.h],[],[with_xen_inotify=no])
|
||||||
|
fi
|
||||||
|
if test "$with_xen_inotify" = "yes"; then
|
||||||
|
AC_DEFINE_UNQUOTED([WITH_XEN_INOTIFY], 1,[whether Xen inotify sub-driver is enabled])
|
||||||
|
fi
|
||||||
|
AM_CONDITIONAL([WITH_XEN_INOTIFY], [test "$with_xen_inotify" = "yes"])
|
||||||
|
|
||||||
dnl
|
dnl
|
||||||
dnl check for kernel headers required by src/bridge.c
|
dnl check for kernel headers required by src/bridge.c
|
||||||
dnl
|
dnl
|
||||||
|
@ -308,7 +308,7 @@ int main(int argc, char **argv)
|
|||||||
myEventRemoveTimeoutFunc);
|
myEventRemoveTimeoutFunc);
|
||||||
|
|
||||||
virConnectPtr dconn = NULL;
|
virConnectPtr dconn = NULL;
|
||||||
dconn = virConnectOpen (argv[1] ? argv[1] : "qemu:///system");
|
dconn = virConnectOpen (argv[1] ? argv[1] : NULL);
|
||||||
if (!dconn) {
|
if (!dconn) {
|
||||||
printf("error opening\n");
|
printf("error opening\n");
|
||||||
return -1;
|
return -1;
|
||||||
|
@ -60,6 +60,7 @@ typedef enum {
|
|||||||
VIR_FROM_DOMAIN, /* Error from domain config */
|
VIR_FROM_DOMAIN, /* Error from domain config */
|
||||||
VIR_FROM_UML, /* Error at the UML driver */
|
VIR_FROM_UML, /* Error at the UML driver */
|
||||||
VIR_FROM_NODEDEV, /* Error from node device monitor */
|
VIR_FROM_NODEDEV, /* Error from node device monitor */
|
||||||
|
VIR_FROM_XEN_INOTIFY, /* Error from xen inotify layer */
|
||||||
} virErrorDomain;
|
} virErrorDomain;
|
||||||
|
|
||||||
|
|
||||||
|
@ -35,6 +35,7 @@ src/util.c
|
|||||||
src/uuid.c
|
src/uuid.c
|
||||||
src/virsh.c
|
src/virsh.c
|
||||||
src/virterror.c
|
src/virterror.c
|
||||||
|
src/xen_inotify.c
|
||||||
src/xen_internal.c
|
src/xen_internal.c
|
||||||
src/xend_internal.c
|
src/xend_internal.c
|
||||||
src/xm_internal.c
|
src/xm_internal.c
|
||||||
|
@ -1577,7 +1577,8 @@ getLibvirtModuleObject (void) {
|
|||||||
return libvirt_module;
|
return libvirt_module;
|
||||||
|
|
||||||
// PyImport_ImportModule returns a new reference
|
// PyImport_ImportModule returns a new reference
|
||||||
libvirt_module = PyImport_ImportModule("libvirt");
|
/* Bogus (char *) cast for RHEL-5 python API brokenness */
|
||||||
|
libvirt_module = PyImport_ImportModule((char *)"libvirt");
|
||||||
if(!libvirt_module) {
|
if(!libvirt_module) {
|
||||||
#if DEBUG_ERROR
|
#if DEBUG_ERROR
|
||||||
printf("%s Error importing libvirt module\n", __FUNCTION__);
|
printf("%s Error importing libvirt module\n", __FUNCTION__);
|
||||||
|
@ -101,6 +101,9 @@ XEN_DRIVER_SOURCES = \
|
|||||||
xend_internal.c xend_internal.h \
|
xend_internal.c xend_internal.h \
|
||||||
xm_internal.c xm_internal.h \
|
xm_internal.c xm_internal.h \
|
||||||
xs_internal.c xs_internal.h
|
xs_internal.c xs_internal.h
|
||||||
|
if WITH_XEN_INOTIFY
|
||||||
|
XEN_DRIVER_SOURCES += xen_inotify.c xen_inotify.h
|
||||||
|
endif
|
||||||
|
|
||||||
LXC_DRIVER_SOURCES = \
|
LXC_DRIVER_SOURCES = \
|
||||||
lxc_conf.c lxc_conf.h \
|
lxc_conf.c lxc_conf.h \
|
||||||
@ -189,6 +192,8 @@ libvirt_driver_la_SOURCES = \
|
|||||||
libvirt_driver_la_CFLAGS = $(XEN_CFLAGS)
|
libvirt_driver_la_CFLAGS = $(XEN_CFLAGS)
|
||||||
libvirt_driver_la_LDFLAGS = $(XEN_LIBS)
|
libvirt_driver_la_LDFLAGS = $(XEN_LIBS)
|
||||||
|
|
||||||
|
libvirt_driver_la_CFLAGS = $(XEN_CFLAGS)
|
||||||
|
|
||||||
if WITH_TEST
|
if WITH_TEST
|
||||||
if WITH_DRIVER_MODULES
|
if WITH_DRIVER_MODULES
|
||||||
mod_LTLIBRARIES += libvirt_driver_test.la
|
mod_LTLIBRARIES += libvirt_driver_test.la
|
||||||
|
@ -613,6 +613,10 @@ virRun(virConnectPtr conn,
|
|||||||
VIR_FREE(outbuf);
|
VIR_FREE(outbuf);
|
||||||
VIR_FREE(errbuf);
|
VIR_FREE(errbuf);
|
||||||
VIR_FREE(argv_str);
|
VIR_FREE(argv_str);
|
||||||
|
if (outfd != -1)
|
||||||
|
close(outfd);
|
||||||
|
if (errfd != -1)
|
||||||
|
close(errfd);
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -262,6 +262,9 @@ virDefaultErrorFunc(virErrorPtr err)
|
|||||||
case VIR_FROM_XENSTORE:
|
case VIR_FROM_XENSTORE:
|
||||||
dom = "Xen Store ";
|
dom = "Xen Store ";
|
||||||
break;
|
break;
|
||||||
|
case VIR_FROM_XEN_INOTIFY:
|
||||||
|
dom = "Xen Inotify ";
|
||||||
|
break;
|
||||||
case VIR_FROM_DOM:
|
case VIR_FROM_DOM:
|
||||||
dom = "Domain ";
|
dom = "Domain ";
|
||||||
break;
|
break;
|
||||||
|
458
src/xen_inotify.c
Normal file
458
src/xen_inotify.c
Normal file
@ -0,0 +1,458 @@
|
|||||||
|
/*
|
||||||
|
* xen_inofify.c: Xen notification of xml file activity in the
|
||||||
|
* following dirs:
|
||||||
|
* /etc/xen
|
||||||
|
* /var/lib/xend/domains
|
||||||
|
*
|
||||||
|
* Copyright (C) 2008 VirtualIron
|
||||||
|
*
|
||||||
|
* 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: Ben Guthro
|
||||||
|
*/
|
||||||
|
#include <config.h>
|
||||||
|
#include <dirent.h>
|
||||||
|
#include <sys/inotify.h>
|
||||||
|
|
||||||
|
#include "virterror_internal.h"
|
||||||
|
#include "datatypes.h"
|
||||||
|
#include "driver.h"
|
||||||
|
#include "memory.h"
|
||||||
|
#include "event.h"
|
||||||
|
#include "xen_unified.h"
|
||||||
|
#include "conf.h"
|
||||||
|
#include "domain_conf.h"
|
||||||
|
#include "xen_inotify.h"
|
||||||
|
#include "xend_internal.h"
|
||||||
|
#include "logging.h"
|
||||||
|
#include "uuid.h"
|
||||||
|
|
||||||
|
#include "xm_internal.h" /* for xenXMDomainConfigParse */
|
||||||
|
|
||||||
|
#define virXenInotifyError(conn, code, fmt...) \
|
||||||
|
virReportErrorHelper(NULL, VIR_FROM_XEN_INOTIFY, code, __FILE__, \
|
||||||
|
__FUNCTION__, __LINE__, fmt)
|
||||||
|
|
||||||
|
#define LIBVIRTD_DOMAINS_DIR "/var/lib/xend/domains"
|
||||||
|
static const char *configDir = NULL;
|
||||||
|
static int useXenConfigCache = 0;
|
||||||
|
static xenUnifiedDomainInfoListPtr configInfoList = NULL;
|
||||||
|
|
||||||
|
struct xenUnifiedDriver xenInotifyDriver = {
|
||||||
|
xenInotifyOpen, /* open */
|
||||||
|
xenInotifyClose, /* close */
|
||||||
|
NULL, /* version */
|
||||||
|
NULL, /* hostname */
|
||||||
|
NULL, /* URI */
|
||||||
|
NULL, /* nodeGetInfo */
|
||||||
|
NULL, /* getCapabilities */
|
||||||
|
NULL, /* listDomains */
|
||||||
|
NULL, /* numOfDomains */
|
||||||
|
NULL, /* domainCreateLinux */
|
||||||
|
NULL, /* domainSuspend */
|
||||||
|
NULL, /* domainResume */
|
||||||
|
NULL, /* domainShutdown */
|
||||||
|
NULL, /* domainReboot */
|
||||||
|
NULL, /* domainDestroy */
|
||||||
|
NULL, /* domainGetOSType */
|
||||||
|
NULL, /* domainGetMaxMemory */
|
||||||
|
NULL, /* domainSetMaxMemory */
|
||||||
|
NULL, /* domainSetMemory */
|
||||||
|
NULL, /* 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 */
|
||||||
|
};
|
||||||
|
|
||||||
|
static virDomainPtr
|
||||||
|
xenInotifyXenCacheLookup(virConnectPtr conn, const char *filename) {
|
||||||
|
xenXMConfCachePtr entry;
|
||||||
|
virDomainPtr dom;
|
||||||
|
|
||||||
|
if (!(entry = virHashLookup(xenXMGetConfigCache(), filename))) {
|
||||||
|
DEBUG("No config found for %s", filename);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(!(dom = virGetDomain(conn, entry->def->name,
|
||||||
|
(unsigned char*)entry->def->uuid))) {
|
||||||
|
DEBUG0("Error getting dom from def");
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
return dom;
|
||||||
|
}
|
||||||
|
|
||||||
|
static virDomainPtr
|
||||||
|
xenInotifyXendDomainsDirLookup(virConnectPtr conn, const char *filename) {
|
||||||
|
int i;
|
||||||
|
virDomainPtr dom;
|
||||||
|
const char *uuid_str;
|
||||||
|
unsigned char uuid[VIR_UUID_BUFLEN];
|
||||||
|
|
||||||
|
/* xend is managing domains. we will get
|
||||||
|
* a filename in the manner:
|
||||||
|
* /var/lib/xend/domains/<uuid>/
|
||||||
|
*/
|
||||||
|
uuid_str = filename + strlen(LIBVIRTD_DOMAINS_DIR) + 1;
|
||||||
|
|
||||||
|
if (virUUIDParse(uuid_str, uuid) < 0) {
|
||||||
|
virXenInotifyError(NULL, VIR_ERR_INTERNAL_ERROR,
|
||||||
|
"parsing uuid %s", uuid_str);
|
||||||
|
return (NULL);
|
||||||
|
}
|
||||||
|
/* call directly into xend here, as driver may not yet
|
||||||
|
be set during open while we are building our
|
||||||
|
initial list of domains */
|
||||||
|
DEBUG("Looking for dom with uuid: %s", uuid_str);
|
||||||
|
if(!(dom = xenDaemonLookupByUUID(conn, uuid))) {
|
||||||
|
/* If we are here, the domain has gone away.
|
||||||
|
search for, and create a domain from the stored
|
||||||
|
list info */
|
||||||
|
for (i=0; i<configInfoList->count; i++) {
|
||||||
|
if (!memcmp(uuid, configInfoList->doms[i]->uuid, VIR_UUID_BUFLEN)) {
|
||||||
|
if(!(dom = virGetDomain(conn, configInfoList->doms[i]->name,
|
||||||
|
configInfoList->doms[i]->uuid))) {
|
||||||
|
virXenInotifyError(NULL, VIR_ERR_INTERNAL_ERROR,
|
||||||
|
"finding dom for %s", uuid_str);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
DEBUG0("Found dom on list");
|
||||||
|
return dom;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
virXenInotifyError(NULL, VIR_ERR_INTERNAL_ERROR,
|
||||||
|
"%s", _("finding dom on config list"));
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* succeeded too find domain by uuid */
|
||||||
|
return dom;
|
||||||
|
}
|
||||||
|
|
||||||
|
static virDomainPtr
|
||||||
|
xenInotifyDomainLookup(virConnectPtr conn, const char *filename) {
|
||||||
|
virDomainPtr dom;
|
||||||
|
virDomainInfo info;
|
||||||
|
|
||||||
|
dom = useXenConfigCache ? xenInotifyXenCacheLookup(conn, filename) :
|
||||||
|
xenInotifyXendDomainsDirLookup(conn, filename);
|
||||||
|
|
||||||
|
if(dom) {
|
||||||
|
if ( (useXenConfigCache ? xenXMDomainGetInfo(dom, &info) :
|
||||||
|
xenDaemonDomainGetInfo(dom, &info)) < 0)
|
||||||
|
dom->id = -1;
|
||||||
|
else
|
||||||
|
dom->id = (info.state == VIR_DOMAIN_SHUTOFF) ? -1 : dom->id;
|
||||||
|
return dom;
|
||||||
|
}
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int
|
||||||
|
xenInotifyXendDomainsDirRemoveEntry(virConnectPtr conn ATTRIBUTE_UNUSED,
|
||||||
|
const char *fname) {
|
||||||
|
const char *uuidstr = fname + strlen(LIBVIRTD_DOMAINS_DIR) + 1;
|
||||||
|
unsigned char uuid[VIR_UUID_BUFLEN];
|
||||||
|
int i;
|
||||||
|
|
||||||
|
if (virUUIDParse(uuidstr, uuid) < 0) {
|
||||||
|
virXenInotifyError(NULL, VIR_ERR_INTERNAL_ERROR,
|
||||||
|
"parsing uuid %s", uuidstr);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* match and remove on uuid */
|
||||||
|
for (i=0; i<configInfoList->count; i++) {
|
||||||
|
if (!memcmp(uuid, configInfoList->doms[i]->uuid, VIR_UUID_BUFLEN)) {
|
||||||
|
VIR_FREE(configInfoList->doms[i]->name);
|
||||||
|
VIR_FREE(configInfoList->doms[i]);
|
||||||
|
|
||||||
|
if (i < (configInfoList->count - 1))
|
||||||
|
memmove(configInfoList->doms + i,
|
||||||
|
configInfoList->doms + i + 1,
|
||||||
|
sizeof(*(configInfoList->doms)) *
|
||||||
|
(configInfoList->count - (i + 1)));
|
||||||
|
|
||||||
|
if (VIR_REALLOC_N(configInfoList->doms,
|
||||||
|
configInfoList->count - 1) < 0) {
|
||||||
|
; /* Failure to reduce memory allocation isn't fatal */
|
||||||
|
}
|
||||||
|
configInfoList->count--;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int
|
||||||
|
xenInotifyXendDomainsDirAddEntry(virConnectPtr conn,
|
||||||
|
const char *fname) {
|
||||||
|
virDomainPtr dom = xenInotifyDomainLookup(conn, fname);
|
||||||
|
if(!dom) {
|
||||||
|
virXenInotifyError(NULL, VIR_ERR_INTERNAL_ERROR,
|
||||||
|
"%s", _("Error looking up domain"));
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if( xenUnifiedAddDomainInfo(configInfoList,
|
||||||
|
dom->id, dom->name, dom->uuid) < 0) {
|
||||||
|
virXenInotifyError(NULL, VIR_ERR_INTERNAL_ERROR,
|
||||||
|
"%s", _("Error adding file to config cache"));
|
||||||
|
virUnrefDomain(dom);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
virUnrefDomain(dom);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int
|
||||||
|
xenInotifyRemoveDomainConfigInfo(virConnectPtr conn,
|
||||||
|
const char *fname) {
|
||||||
|
return useXenConfigCache ? xenXMConfigCacheRemoveFile(conn, fname) :
|
||||||
|
xenInotifyXendDomainsDirRemoveEntry(conn, fname);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int
|
||||||
|
xenInotifyAddDomainConfigInfo(virConnectPtr conn,
|
||||||
|
const char *fname) {
|
||||||
|
return useXenConfigCache ? xenXMConfigCacheAddFile(conn, fname) :
|
||||||
|
xenInotifyXendDomainsDirAddEntry(conn, fname);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
xenInotifyEvent(int watch ATTRIBUTE_UNUSED,
|
||||||
|
int fd,
|
||||||
|
int events ATTRIBUTE_UNUSED,
|
||||||
|
void *data)
|
||||||
|
{
|
||||||
|
char buf[1024];
|
||||||
|
char fname[1024];
|
||||||
|
struct inotify_event *e;
|
||||||
|
int got;
|
||||||
|
char *tmp, *name;
|
||||||
|
virConnectPtr conn = (virConnectPtr) data;
|
||||||
|
xenUnifiedPrivatePtr priv = NULL;
|
||||||
|
virDomainPtr dom = NULL;
|
||||||
|
|
||||||
|
DEBUG0("got inotify event");
|
||||||
|
|
||||||
|
if( conn && conn->privateData ) {
|
||||||
|
priv = conn->privateData;
|
||||||
|
} else {
|
||||||
|
virXenInotifyError(NULL, VIR_ERR_INTERNAL_ERROR,
|
||||||
|
"%s", _("conn, or private data is NULL"));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
reread:
|
||||||
|
got = read(fd, buf, sizeof(buf));
|
||||||
|
if (got == -1) {
|
||||||
|
if (errno == EINTR)
|
||||||
|
goto reread;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
tmp = buf;
|
||||||
|
while (got) {
|
||||||
|
if (got < sizeof(struct inotify_event))
|
||||||
|
return; /* bad */
|
||||||
|
|
||||||
|
e = (struct inotify_event *)tmp;
|
||||||
|
tmp += sizeof(struct inotify_event);
|
||||||
|
got -= sizeof(struct inotify_event);
|
||||||
|
|
||||||
|
if (got < e->len)
|
||||||
|
return;
|
||||||
|
|
||||||
|
tmp += e->len;
|
||||||
|
got -= e->len;
|
||||||
|
|
||||||
|
name = (char *)&(e->name);
|
||||||
|
|
||||||
|
snprintf(fname, 1024, "%s/%s", configDir, name);
|
||||||
|
|
||||||
|
if (e->mask & (IN_DELETE | IN_MOVED_FROM)) {
|
||||||
|
if (!(dom = xenInotifyDomainLookup(conn, fname))) {
|
||||||
|
virXenInotifyError(NULL, VIR_ERR_INTERNAL_ERROR,
|
||||||
|
"%s", _("looking up dom"));
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
xenUnifiedDomainEventDispatch(conn->privateData, dom,
|
||||||
|
VIR_DOMAIN_EVENT_UNDEFINED,
|
||||||
|
VIR_DOMAIN_EVENT_UNDEFINED_REMOVED);
|
||||||
|
|
||||||
|
|
||||||
|
if (xenInotifyRemoveDomainConfigInfo(conn, fname) < 0 ) {
|
||||||
|
virXenInotifyError(NULL, VIR_ERR_INTERNAL_ERROR,
|
||||||
|
"%s", _("Error adding file to config cache"));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
} else if (e->mask & ( IN_CREATE | IN_CLOSE_WRITE | IN_MOVED_TO) ) {
|
||||||
|
if (xenInotifyAddDomainConfigInfo(conn, fname) < 0 ) {
|
||||||
|
virXenInotifyError(NULL, VIR_ERR_INTERNAL_ERROR,
|
||||||
|
"%s", _("Error adding file to config cache"));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!(dom = xenInotifyDomainLookup(conn, fname))) {
|
||||||
|
virXenInotifyError(NULL, VIR_ERR_INTERNAL_ERROR,
|
||||||
|
"%s", _("looking up dom"));
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
xenUnifiedDomainEventDispatch(conn->privateData, dom,
|
||||||
|
VIR_DOMAIN_EVENT_DEFINED,
|
||||||
|
VIR_DOMAIN_EVENT_DEFINED_ADDED);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* xenInotifyOpen:
|
||||||
|
* @conn: pointer to the connection block
|
||||||
|
* @name: URL for the target, NULL for local
|
||||||
|
* @flags: combination of virDrvOpenFlag(s)
|
||||||
|
*
|
||||||
|
* Connects and starts listening for inotify events
|
||||||
|
*
|
||||||
|
* Returns 0 or -1 in case of error.
|
||||||
|
*/
|
||||||
|
int
|
||||||
|
xenInotifyOpen(virConnectPtr conn ATTRIBUTE_UNUSED,
|
||||||
|
virConnectAuthPtr auth ATTRIBUTE_UNUSED,
|
||||||
|
int flags ATTRIBUTE_UNUSED)
|
||||||
|
{
|
||||||
|
DIR *dh;
|
||||||
|
struct dirent *ent;
|
||||||
|
char path[PATH_MAX];
|
||||||
|
xenUnifiedPrivatePtr priv = (xenUnifiedPrivatePtr) conn->privateData;
|
||||||
|
|
||||||
|
if(priv->xendConfigVersion <= 2) {
|
||||||
|
/* /etc/xen */
|
||||||
|
configDir = xenXMGetConfigDir();
|
||||||
|
useXenConfigCache = 1;
|
||||||
|
} else {
|
||||||
|
/* /var/lib/xend/domains/<uuid>/config.sxp */
|
||||||
|
configDir = LIBVIRTD_DOMAINS_DIR;
|
||||||
|
useXenConfigCache = 0;
|
||||||
|
|
||||||
|
if ( VIR_ALLOC(configInfoList ) < 0) {
|
||||||
|
virXenInotifyError(NULL, VIR_ERR_INTERNAL_ERROR,
|
||||||
|
"%s", _("failed to allocate configInfoList"));
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* populate initial list */
|
||||||
|
if (!(dh = opendir(configDir))) {
|
||||||
|
virXenInotifyError (NULL, VIR_ERR_INTERNAL_ERROR,
|
||||||
|
"%s", strerror(errno));
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
while ((ent = readdir(dh))) {
|
||||||
|
if (STRPREFIX(ent->d_name, "."))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
/* Build the full file path */
|
||||||
|
if ((strlen(configDir) + 1 + strlen(ent->d_name) + 1) > PATH_MAX)
|
||||||
|
continue;
|
||||||
|
strcpy(path, configDir);
|
||||||
|
strcat(path, "/");
|
||||||
|
strcat(path, ent->d_name);
|
||||||
|
|
||||||
|
if (xenInotifyAddDomainConfigInfo(conn, path) < 0 ) {
|
||||||
|
virXenInotifyError(NULL, VIR_ERR_INTERNAL_ERROR,
|
||||||
|
"%s", _("Error adding file to config list"));
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((priv->inotifyFD = inotify_init()) < 0) {
|
||||||
|
virXenInotifyError(NULL, VIR_ERR_INTERNAL_ERROR,
|
||||||
|
"%s", _("initializing inotify"));
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
DEBUG("Adding a watch on %s", configDir);
|
||||||
|
if (inotify_add_watch(priv->inotifyFD,
|
||||||
|
configDir,
|
||||||
|
IN_CREATE |
|
||||||
|
IN_CLOSE_WRITE | IN_DELETE |
|
||||||
|
IN_MOVED_TO | IN_MOVED_FROM) < 0) {
|
||||||
|
virXenInotifyError(NULL, VIR_ERR_INTERNAL_ERROR,
|
||||||
|
"adding watch on %s", _(configDir));
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
DEBUG0("Building initial config cache");
|
||||||
|
if (useXenConfigCache &&
|
||||||
|
xenXMConfigCacheRefresh (conn) < 0) {
|
||||||
|
DEBUG("Failed to enable XM config cache %s", conn->err.message);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
DEBUG0("Registering with event loop");
|
||||||
|
/* Add the handle for monitoring */
|
||||||
|
if ((priv->inotifyWatch = virEventAddHandle(priv->inotifyFD, VIR_EVENT_HANDLE_READABLE,
|
||||||
|
xenInotifyEvent, conn, NULL)) < 0) {
|
||||||
|
DEBUG0("Failed to add inotify handle, disabling events");
|
||||||
|
}
|
||||||
|
|
||||||
|
conn->refs++;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* xenInotifyClose:
|
||||||
|
* @conn: pointer to the connection block
|
||||||
|
*
|
||||||
|
* Close and stop listening for inotify events
|
||||||
|
*
|
||||||
|
* Returns 0 in case of success or -1 in case of error.
|
||||||
|
*/
|
||||||
|
int
|
||||||
|
xenInotifyClose(virConnectPtr conn)
|
||||||
|
{
|
||||||
|
xenUnifiedPrivatePtr priv = (xenUnifiedPrivatePtr) conn->privateData;
|
||||||
|
|
||||||
|
if(configInfoList)
|
||||||
|
xenUnifiedDomainInfoListFree(configInfoList);
|
||||||
|
|
||||||
|
if (priv->inotifyWatch != -1)
|
||||||
|
virEventRemoveHandle(priv->inotifyWatch);
|
||||||
|
close(priv->inotifyFD);
|
||||||
|
virUnrefConnect(conn);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
31
src/xen_inotify.h
Normal file
31
src/xen_inotify.h
Normal file
@ -0,0 +1,31 @@
|
|||||||
|
/*
|
||||||
|
* xen_inofify.h: Xen notification of xml files
|
||||||
|
*
|
||||||
|
* Copyright (C) 2008 VirtualIron
|
||||||
|
*
|
||||||
|
* 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: Ben Guthro
|
||||||
|
*/
|
||||||
|
#ifndef __VIR_XEN_INOTIFY_H__
|
||||||
|
#define __VIR_XEN_INOTIFY_H__
|
||||||
|
#include "internal.h"
|
||||||
|
extern struct xenUnifiedDriver xenInotifyDriver;
|
||||||
|
|
||||||
|
int xenInotifyOpen (virConnectPtr conn,
|
||||||
|
virConnectAuthPtr auth,
|
||||||
|
int flags);
|
||||||
|
int xenInotifyClose (virConnectPtr conn);
|
||||||
|
#endif
|
@ -37,6 +37,9 @@
|
|||||||
#include "xend_internal.h"
|
#include "xend_internal.h"
|
||||||
#include "xs_internal.h"
|
#include "xs_internal.h"
|
||||||
#include "xm_internal.h"
|
#include "xm_internal.h"
|
||||||
|
#if WITH_XEN_INOTIFY
|
||||||
|
#include "xen_inotify.h"
|
||||||
|
#endif
|
||||||
#include "xml.h"
|
#include "xml.h"
|
||||||
#include "util.h"
|
#include "util.h"
|
||||||
#include "memory.h"
|
#include "memory.h"
|
||||||
@ -57,6 +60,9 @@ static struct xenUnifiedDriver *drivers[XEN_UNIFIED_NR_DRIVERS] = {
|
|||||||
[XEN_UNIFIED_XEND_OFFSET] = &xenDaemonDriver,
|
[XEN_UNIFIED_XEND_OFFSET] = &xenDaemonDriver,
|
||||||
[XEN_UNIFIED_XS_OFFSET] = &xenStoreDriver,
|
[XEN_UNIFIED_XS_OFFSET] = &xenStoreDriver,
|
||||||
[XEN_UNIFIED_XM_OFFSET] = &xenXMDriver,
|
[XEN_UNIFIED_XM_OFFSET] = &xenXMDriver,
|
||||||
|
#if WITH_XEN_INOTIFY
|
||||||
|
[XEN_UNIFIED_INOTIFY_OFFSET] = &xenInotifyDriver,
|
||||||
|
#endif
|
||||||
};
|
};
|
||||||
|
|
||||||
#define xenUnifiedError(conn, code, fmt...) \
|
#define xenUnifiedError(conn, code, fmt...) \
|
||||||
@ -223,6 +229,7 @@ xenUnifiedOpen (virConnectPtr conn, virConnectAuthPtr auth, int flags)
|
|||||||
{
|
{
|
||||||
int i, ret = VIR_DRV_OPEN_DECLINED;
|
int i, ret = VIR_DRV_OPEN_DECLINED;
|
||||||
xenUnifiedPrivatePtr priv;
|
xenUnifiedPrivatePtr priv;
|
||||||
|
virDomainEventCallbackListPtr cbList;
|
||||||
|
|
||||||
if (conn->uri == NULL) {
|
if (conn->uri == NULL) {
|
||||||
if (!xenUnifiedProbe())
|
if (!xenUnifiedProbe())
|
||||||
@ -260,6 +267,13 @@ xenUnifiedOpen (virConnectPtr conn, virConnectAuthPtr auth, int flags)
|
|||||||
}
|
}
|
||||||
conn->privateData = priv;
|
conn->privateData = priv;
|
||||||
|
|
||||||
|
/* Allocate callback list */
|
||||||
|
if (VIR_ALLOC(cbList) < 0) {
|
||||||
|
xenUnifiedError (NULL, VIR_ERR_NO_MEMORY, "allocating callback list");
|
||||||
|
return VIR_DRV_OPEN_ERROR;
|
||||||
|
}
|
||||||
|
priv->domainEventCallbacks = cbList;
|
||||||
|
|
||||||
priv->handle = -1;
|
priv->handle = -1;
|
||||||
priv->xendConfigVersion = -1;
|
priv->xendConfigVersion = -1;
|
||||||
priv->type = -1;
|
priv->type = -1;
|
||||||
@ -333,6 +347,15 @@ xenUnifiedOpen (virConnectPtr conn, virConnectAuthPtr auth, int flags)
|
|||||||
goto fail;
|
goto fail;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if WITH_XEN_INOTIFY
|
||||||
|
DEBUG0("Trying Xen inotify sub-driver");
|
||||||
|
if (drivers[XEN_UNIFIED_INOTIFY_OFFSET]->open(conn, auth, flags) ==
|
||||||
|
VIR_DRV_OPEN_SUCCESS) {
|
||||||
|
DEBUG0("Activated Xen inotify sub-driver");
|
||||||
|
priv->opened[XEN_UNIFIED_INOTIFY_OFFSET] = 1;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
return VIR_DRV_OPEN_SUCCESS;
|
return VIR_DRV_OPEN_SUCCESS;
|
||||||
|
|
||||||
fail:
|
fail:
|
||||||
@ -357,6 +380,8 @@ xenUnifiedClose (virConnectPtr conn)
|
|||||||
int i;
|
int i;
|
||||||
|
|
||||||
virCapabilitiesFree(priv->caps);
|
virCapabilitiesFree(priv->caps);
|
||||||
|
virDomainEventCallbackListFree(priv->domainEventCallbacks);
|
||||||
|
|
||||||
for (i = 0; i < XEN_UNIFIED_NR_DRIVERS; ++i)
|
for (i = 0; i < XEN_UNIFIED_NR_DRIVERS; ++i)
|
||||||
if (priv->opened[i] && drivers[i]->close)
|
if (priv->opened[i] && drivers[i]->close)
|
||||||
(void) drivers[i]->close (conn);
|
(void) drivers[i]->close (conn);
|
||||||
@ -1292,6 +1317,40 @@ xenUnifiedNodeGetFreeMemory (virConnectPtr conn)
|
|||||||
return(0);
|
return(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int
|
||||||
|
xenUnifiedDomainEventRegister (virConnectPtr conn,
|
||||||
|
void *callback,
|
||||||
|
void *opaque,
|
||||||
|
void (*freefunc)(void *))
|
||||||
|
{
|
||||||
|
GET_PRIVATE (conn);
|
||||||
|
if (priv->xsWatch == -1) {
|
||||||
|
xenUnifiedError (conn, VIR_ERR_NO_SUPPORT, __FUNCTION__);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
conn->refs++;
|
||||||
|
return virDomainEventCallbackListAdd(conn, priv->domainEventCallbacks,
|
||||||
|
callback, opaque, freefunc);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int
|
||||||
|
xenUnifiedDomainEventDeregister (virConnectPtr conn,
|
||||||
|
void *callback)
|
||||||
|
{
|
||||||
|
int ret;
|
||||||
|
GET_PRIVATE (conn);
|
||||||
|
if (priv->xsWatch == -1) {
|
||||||
|
xenUnifiedError (conn, VIR_ERR_NO_SUPPORT, __FUNCTION__);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
ret = virDomainEventCallbackListRemove(conn, priv->domainEventCallbacks,
|
||||||
|
callback);
|
||||||
|
virUnrefConnect(conn);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
/*----- Register with libvirt.c, and initialise Xen drivers. -----*/
|
/*----- Register with libvirt.c, and initialise Xen drivers. -----*/
|
||||||
|
|
||||||
#define HV_VERSION ((DOM0_INTERFACE_VERSION >> 24) * 1000000 + \
|
#define HV_VERSION ((DOM0_INTERFACE_VERSION >> 24) * 1000000 + \
|
||||||
@ -1356,6 +1415,8 @@ static virDriver xenUnifiedDriver = {
|
|||||||
.domainBlockPeek = xenUnifiedDomainBlockPeek,
|
.domainBlockPeek = xenUnifiedDomainBlockPeek,
|
||||||
.nodeGetCellsFreeMemory = xenUnifiedNodeGetCellsFreeMemory,
|
.nodeGetCellsFreeMemory = xenUnifiedNodeGetCellsFreeMemory,
|
||||||
.getFreeMemory = xenUnifiedNodeGetFreeMemory,
|
.getFreeMemory = xenUnifiedNodeGetFreeMemory,
|
||||||
|
.domainEventRegister = xenUnifiedDomainEventRegister,
|
||||||
|
.domainEventDeregister = xenUnifiedDomainEventDeregister,
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -1375,3 +1436,139 @@ xenRegister (void)
|
|||||||
return virRegisterDriver (&xenUnifiedDriver);
|
return virRegisterDriver (&xenUnifiedDriver);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* xenUnifiedDomainInfoListFree:
|
||||||
|
*
|
||||||
|
* Free the Domain Info List
|
||||||
|
*/
|
||||||
|
void
|
||||||
|
xenUnifiedDomainInfoListFree(xenUnifiedDomainInfoListPtr list)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
for (i=0; i<list->count; i++) {
|
||||||
|
VIR_FREE(list->doms[i]->name);
|
||||||
|
VIR_FREE(list->doms[i]);
|
||||||
|
}
|
||||||
|
VIR_FREE(list);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* xenUnifiedAddDomainInfo:
|
||||||
|
*
|
||||||
|
* Add name and uuid to the domain info list
|
||||||
|
*
|
||||||
|
* Returns: 0 on success, -1 on failure
|
||||||
|
*/
|
||||||
|
int
|
||||||
|
xenUnifiedAddDomainInfo(xenUnifiedDomainInfoListPtr list,
|
||||||
|
int id, char *name,
|
||||||
|
unsigned char *uuid)
|
||||||
|
{
|
||||||
|
xenUnifiedDomainInfoPtr info;
|
||||||
|
int n;
|
||||||
|
|
||||||
|
/* check if we already have this callback on our list */
|
||||||
|
for (n=0; n < list->count; n++) {
|
||||||
|
if (STREQ(list->doms[n]->name, name) &&
|
||||||
|
!memcmp(list->doms[n]->uuid, uuid, VIR_UUID_BUFLEN)) {
|
||||||
|
DEBUG0("WARNING: dom already tracked");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (VIR_ALLOC(info) < 0)
|
||||||
|
goto memory_error;
|
||||||
|
if (!(info->name = strdup(name)))
|
||||||
|
goto memory_error;
|
||||||
|
|
||||||
|
memcpy(info->uuid, uuid, VIR_UUID_BUFLEN);
|
||||||
|
info->id = id;
|
||||||
|
|
||||||
|
/* Make space on list */
|
||||||
|
n = list->count;
|
||||||
|
if (VIR_REALLOC_N(list->doms, n + 1) < 0) {
|
||||||
|
goto memory_error;
|
||||||
|
}
|
||||||
|
|
||||||
|
list->doms[n] = info;
|
||||||
|
list->count++;
|
||||||
|
return 0;
|
||||||
|
memory_error:
|
||||||
|
xenUnifiedError (NULL, VIR_ERR_NO_MEMORY, "allocating domain info");
|
||||||
|
if (info)
|
||||||
|
VIR_FREE(info->name);
|
||||||
|
VIR_FREE(info);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* xenUnifiedRemoveDomainInfo:
|
||||||
|
*
|
||||||
|
* Removes name and uuid to the domain info list
|
||||||
|
*
|
||||||
|
* Returns: 0 on success, -1 on failure
|
||||||
|
*/
|
||||||
|
int
|
||||||
|
xenUnifiedRemoveDomainInfo(xenUnifiedDomainInfoListPtr list,
|
||||||
|
int id, char *name,
|
||||||
|
unsigned char *uuid)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
for (i = 0 ; i < list->count ; i++) {
|
||||||
|
if( list->doms[i]->id == id &&
|
||||||
|
STREQ(list->doms[i]->name, name) &&
|
||||||
|
!memcmp(list->doms[i]->uuid, uuid, VIR_UUID_BUFLEN)) {
|
||||||
|
|
||||||
|
VIR_FREE(list->doms[i]->name);
|
||||||
|
VIR_FREE(list->doms[i]);
|
||||||
|
|
||||||
|
if (i < (list->count - 1))
|
||||||
|
memmove(list->doms + i,
|
||||||
|
list->doms + i + 1,
|
||||||
|
sizeof(*(list->doms)) *
|
||||||
|
(list->count - (i + 1)));
|
||||||
|
|
||||||
|
if (VIR_REALLOC_N(list->doms,
|
||||||
|
list->count - 1) < 0) {
|
||||||
|
; /* Failure to reduce memory allocation isn't fatal */
|
||||||
|
}
|
||||||
|
list->count--;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* xenUnifiedDomainEventDispatch:
|
||||||
|
*
|
||||||
|
* Dispatch domain events to registered callbacks
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
void xenUnifiedDomainEventDispatch (xenUnifiedPrivatePtr priv,
|
||||||
|
virDomainPtr dom,
|
||||||
|
int event,
|
||||||
|
int detail)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
virDomainEventCallbackListPtr cbList;
|
||||||
|
|
||||||
|
if(!priv) return;
|
||||||
|
|
||||||
|
cbList = priv->domainEventCallbacks;
|
||||||
|
if(!cbList) return;
|
||||||
|
|
||||||
|
for(i=0 ; i < cbList->count ; i++) {
|
||||||
|
if(cbList->callbacks[i] && cbList->callbacks[i]->cb) {
|
||||||
|
if (dom) {
|
||||||
|
DEBUG("Dispatching callback %p %p event %d",
|
||||||
|
cbList->callbacks[i],
|
||||||
|
cbList->callbacks[i]->cb, event);
|
||||||
|
cbList->callbacks[i]->cb(cbList->callbacks[i]->conn,
|
||||||
|
dom, event, detail,
|
||||||
|
cbList->callbacks[i]->opaque);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -14,6 +14,12 @@
|
|||||||
#include "internal.h"
|
#include "internal.h"
|
||||||
#include "capabilities.h"
|
#include "capabilities.h"
|
||||||
#include "driver.h"
|
#include "driver.h"
|
||||||
|
#include "domain_conf.h"
|
||||||
|
#include "xs_internal.h"
|
||||||
|
#if WITH_XEN_INOTIFY
|
||||||
|
#include "xen_inotify.h"
|
||||||
|
#endif
|
||||||
|
#include "domain_event.h"
|
||||||
|
|
||||||
#ifndef HAVE_WINSOCK2_H
|
#ifndef HAVE_WINSOCK2_H
|
||||||
#include <sys/un.h>
|
#include <sys/un.h>
|
||||||
@ -29,7 +35,13 @@ extern int xenRegister (void);
|
|||||||
#define XEN_UNIFIED_XEND_OFFSET 2
|
#define XEN_UNIFIED_XEND_OFFSET 2
|
||||||
#define XEN_UNIFIED_XS_OFFSET 3
|
#define XEN_UNIFIED_XS_OFFSET 3
|
||||||
#define XEN_UNIFIED_XM_OFFSET 4
|
#define XEN_UNIFIED_XM_OFFSET 4
|
||||||
|
|
||||||
|
#if WITH_XEN_INOTIFY
|
||||||
|
#define XEN_UNIFIED_INOTIFY_OFFSET 5
|
||||||
|
#define XEN_UNIFIED_NR_DRIVERS 6
|
||||||
|
#else
|
||||||
#define XEN_UNIFIED_NR_DRIVERS 5
|
#define XEN_UNIFIED_NR_DRIVERS 5
|
||||||
|
#endif
|
||||||
|
|
||||||
#define MIN_XEN_GUEST_SIZE 64 /* 64 megabytes */
|
#define MIN_XEN_GUEST_SIZE 64 /* 64 megabytes */
|
||||||
|
|
||||||
@ -85,6 +97,33 @@ struct xenUnifiedDriver {
|
|||||||
virDrvDomainSetSchedulerParameters domainSetSchedulerParameters;
|
virDrvDomainSetSchedulerParameters domainSetSchedulerParameters;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
typedef struct xenXMConfCache *xenXMConfCachePtr;
|
||||||
|
typedef struct xenXMConfCache {
|
||||||
|
time_t refreshedAt;
|
||||||
|
char filename[PATH_MAX];
|
||||||
|
virDomainDefPtr def;
|
||||||
|
} xenXMConfCache;
|
||||||
|
|
||||||
|
/* xenUnifiedDomainInfoPtr:
|
||||||
|
* The minimal state we have about active domains
|
||||||
|
* This is the minmal info necessary to still get a
|
||||||
|
* virDomainPtr when the domain goes away
|
||||||
|
*/
|
||||||
|
struct _xenUnifiedDomainInfo {
|
||||||
|
int id;
|
||||||
|
char *name;
|
||||||
|
unsigned char uuid[VIR_UUID_BUFLEN];
|
||||||
|
};
|
||||||
|
typedef struct _xenUnifiedDomainInfo xenUnifiedDomainInfo;
|
||||||
|
typedef xenUnifiedDomainInfo *xenUnifiedDomainInfoPtr;
|
||||||
|
|
||||||
|
struct _xenUnifiedDomainInfoList {
|
||||||
|
unsigned int count;
|
||||||
|
xenUnifiedDomainInfoPtr *doms;
|
||||||
|
};
|
||||||
|
typedef struct _xenUnifiedDomainInfoList xenUnifiedDomainInfoList;
|
||||||
|
typedef xenUnifiedDomainInfoList *xenUnifiedDomainInfoListPtr;
|
||||||
|
|
||||||
/* xenUnifiedPrivatePtr:
|
/* xenUnifiedPrivatePtr:
|
||||||
*
|
*
|
||||||
* Per-connection private data, stored in conn->privateData. All Xen
|
* Per-connection private data, stored in conn->privateData. All Xen
|
||||||
@ -113,6 +152,19 @@ struct _xenUnifiedPrivate {
|
|||||||
* xen_unified.c.
|
* xen_unified.c.
|
||||||
*/
|
*/
|
||||||
int opened[XEN_UNIFIED_NR_DRIVERS];
|
int opened[XEN_UNIFIED_NR_DRIVERS];
|
||||||
|
|
||||||
|
/* A list of xenstore watches */
|
||||||
|
xenStoreWatchListPtr xsWatchList;
|
||||||
|
int xsWatch;
|
||||||
|
|
||||||
|
/* An list of callbacks */
|
||||||
|
virDomainEventCallbackListPtr domainEventCallbacks;
|
||||||
|
|
||||||
|
#if WITH_XEN_INOTIFY
|
||||||
|
/* The inotify fd */
|
||||||
|
int inotifyFD;
|
||||||
|
int inotifyWatch;
|
||||||
|
#endif
|
||||||
};
|
};
|
||||||
|
|
||||||
typedef struct _xenUnifiedPrivate *xenUnifiedPrivatePtr;
|
typedef struct _xenUnifiedPrivate *xenUnifiedPrivatePtr;
|
||||||
@ -122,4 +174,15 @@ int xenNbCells(virConnectPtr conn);
|
|||||||
int xenNbCpus(virConnectPtr conn);
|
int xenNbCpus(virConnectPtr conn);
|
||||||
char *xenDomainUsedCpus(virDomainPtr dom);
|
char *xenDomainUsedCpus(virDomainPtr dom);
|
||||||
|
|
||||||
|
void xenUnifiedDomainInfoListFree(xenUnifiedDomainInfoListPtr info);
|
||||||
|
int xenUnifiedAddDomainInfo(xenUnifiedDomainInfoListPtr info,
|
||||||
|
int id, char *name,
|
||||||
|
unsigned char *uuid);
|
||||||
|
int xenUnifiedRemoveDomainInfo(xenUnifiedDomainInfoListPtr info,
|
||||||
|
int id, char *name,
|
||||||
|
unsigned char *uuid);
|
||||||
|
void xenUnifiedDomainEventDispatch (xenUnifiedPrivatePtr priv,
|
||||||
|
virDomainPtr dom,
|
||||||
|
int event,
|
||||||
|
int detail);
|
||||||
#endif /* __VIR_XEN_UNIFIED_H__ */
|
#endif /* __VIR_XEN_UNIFIED_H__ */
|
||||||
|
@ -45,6 +45,8 @@
|
|||||||
#include "uuid.h"
|
#include "uuid.h"
|
||||||
#include "util.h"
|
#include "util.h"
|
||||||
#include "memory.h"
|
#include "memory.h"
|
||||||
|
#include "logging.h"
|
||||||
|
|
||||||
|
|
||||||
/* The true Xen limit varies but so far is always way
|
/* The true Xen limit varies but so far is always way
|
||||||
less than 1024, which is the Linux kernel limit according
|
less than 1024, which is the Linux kernel limit according
|
||||||
@ -54,13 +56,6 @@
|
|||||||
static int xenXMConfigSetString(virConfPtr conf, const char *setting,
|
static int xenXMConfigSetString(virConfPtr conf, const char *setting,
|
||||||
const char *str);
|
const char *str);
|
||||||
|
|
||||||
typedef struct xenXMConfCache *xenXMConfCachePtr;
|
|
||||||
typedef struct xenXMConfCache {
|
|
||||||
time_t refreshedAt;
|
|
||||||
char filename[PATH_MAX];
|
|
||||||
virDomainDefPtr def;
|
|
||||||
} xenXMConfCache;
|
|
||||||
|
|
||||||
static char configDir[PATH_MAX];
|
static char configDir[PATH_MAX];
|
||||||
/* Config file name to config object */
|
/* Config file name to config object */
|
||||||
static virHashTablePtr configCache = NULL;
|
static virHashTablePtr configCache = NULL;
|
||||||
@ -124,6 +119,14 @@ struct xenUnifiedDriver xenXMDriver = {
|
|||||||
NULL, /* domainSetSchedulerParameters */
|
NULL, /* domainSetSchedulerParameters */
|
||||||
};
|
};
|
||||||
|
|
||||||
|
virHashTablePtr xenXMGetConfigCache (void) {
|
||||||
|
return configCache;
|
||||||
|
}
|
||||||
|
|
||||||
|
char *xenXMGetConfigDir (void) {
|
||||||
|
return configDir;
|
||||||
|
}
|
||||||
|
|
||||||
#define xenXMError(conn, code, fmt...) \
|
#define xenXMError(conn, code, fmt...) \
|
||||||
virReportErrorHelper(conn, VIR_FROM_XENXM, code, __FILE__, \
|
virReportErrorHelper(conn, VIR_FROM_XENXM, code, __FILE__, \
|
||||||
__FUNCTION__, __LINE__, fmt)
|
__FUNCTION__, __LINE__, fmt)
|
||||||
@ -371,13 +374,121 @@ xenXMConfigSaveFile(virConnectPtr conn, const char *filename, virDomainDefPtr de
|
|||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int
|
||||||
|
xenXMConfigCacheRemoveFile(virConnectPtr conn ATTRIBUTE_UNUSED,
|
||||||
|
const char *filename)
|
||||||
|
{
|
||||||
|
xenXMConfCachePtr entry;
|
||||||
|
|
||||||
|
entry = virHashLookup(configCache, filename);
|
||||||
|
if (!entry) {
|
||||||
|
DEBUG("No config entry for %s", filename);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
virHashRemoveEntry(nameConfigMap, entry->def->name, NULL);
|
||||||
|
virHashRemoveEntry(configCache, filename, xenXMConfigFree);
|
||||||
|
DEBUG("Removed %s %s", entry->def->name, filename);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
int
|
||||||
|
xenXMConfigCacheAddFile(virConnectPtr conn, const char *filename)
|
||||||
|
{
|
||||||
|
xenXMConfCachePtr entry;
|
||||||
|
struct stat st;
|
||||||
|
int newborn = 0;
|
||||||
|
time_t now = time(NULL);
|
||||||
|
|
||||||
|
DEBUG("Adding file %s", filename);
|
||||||
|
|
||||||
|
/* Get modified time */
|
||||||
|
if ((stat(filename, &st) < 0)) {
|
||||||
|
xenXMError (conn, VIR_ERR_INTERNAL_ERROR,
|
||||||
|
"cannot stat %s: %s", filename, strerror(errno));
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Ignore zero length files, because inotify fires before
|
||||||
|
any content has actually been created */
|
||||||
|
if (st.st_size == 0) {
|
||||||
|
DEBUG("Ignoring zero length file %s", filename);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* If we already have a matching entry and it is not
|
||||||
|
modified, then carry on to next one*/
|
||||||
|
if ((entry = virHashLookup(configCache, filename))) {
|
||||||
|
char *nameowner;
|
||||||
|
|
||||||
|
if (entry->refreshedAt >= st.st_mtime) {
|
||||||
|
entry->refreshedAt = now;
|
||||||
|
/* return success if up-to-date */
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* If we currently own the name, then release it and
|
||||||
|
re-acquire it later - just in case it was renamed */
|
||||||
|
nameowner = (char *)virHashLookup(nameConfigMap, entry->def->name);
|
||||||
|
if (nameowner && STREQ(nameowner, filename)) {
|
||||||
|
virHashRemoveEntry(nameConfigMap, entry->def->name, NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Clear existing config entry which needs refresh */
|
||||||
|
virDomainDefFree(entry->def);
|
||||||
|
entry->def = NULL;
|
||||||
|
} else { /* Completely new entry */
|
||||||
|
newborn = 1;
|
||||||
|
if (VIR_ALLOC(entry) < 0) {
|
||||||
|
xenXMError (conn, VIR_ERR_NO_MEMORY, "%s", strerror(errno));
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
memcpy(entry->filename, filename, PATH_MAX);
|
||||||
|
}
|
||||||
|
entry->refreshedAt = now;
|
||||||
|
|
||||||
|
if (!(entry->def = xenXMConfigReadFile(conn, entry->filename))) {
|
||||||
|
DEBUG("Failed to read %s", entry->filename);
|
||||||
|
if (!newborn)
|
||||||
|
virHashRemoveEntry(configCache, filename, NULL);
|
||||||
|
VIR_FREE(entry);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* If its a completely new entry, it must be stuck into
|
||||||
|
the cache (refresh'd entries are already registered) */
|
||||||
|
if (newborn) {
|
||||||
|
if (virHashAddEntry(configCache, entry->filename, entry) < 0) {
|
||||||
|
virDomainDefFree(entry->def);
|
||||||
|
VIR_FREE(entry);
|
||||||
|
xenXMError (conn, VIR_ERR_INTERNAL_ERROR,
|
||||||
|
"%s", _("xenXMConfigCacheRefresh: virHashAddEntry"));
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* See if we need to map this config file in as the primary owner
|
||||||
|
* of the domain in question
|
||||||
|
*/
|
||||||
|
if (!virHashLookup(nameConfigMap, entry->def->name)) {
|
||||||
|
if (virHashAddEntry(nameConfigMap, entry->def->name, entry->filename) < 0) {
|
||||||
|
virHashRemoveEntry(configCache, filename, NULL);
|
||||||
|
virDomainDefFree(entry->def);
|
||||||
|
VIR_FREE(entry);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
DEBUG("Added config %s %s", entry->def->name, filename);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
/* This method is called by various methods to scan /etc/xen
|
/* This method is called by various methods to scan /etc/xen
|
||||||
(or whatever directory was set by LIBVIRT_XM_CONFIG_DIR
|
(or whatever directory was set by LIBVIRT_XM_CONFIG_DIR
|
||||||
environment variable) and process any domain configs. It
|
environment variable) and process any domain configs. It
|
||||||
has rate-limited so never rescans more frequently than
|
has rate-limited so never rescans more frequently than
|
||||||
once every X seconds */
|
once every X seconds */
|
||||||
static int xenXMConfigCacheRefresh (virConnectPtr conn) {
|
int xenXMConfigCacheRefresh (virConnectPtr conn) {
|
||||||
DIR *dh;
|
DIR *dh;
|
||||||
struct dirent *ent;
|
struct dirent *ent;
|
||||||
time_t now = time(NULL);
|
time_t now = time(NULL);
|
||||||
@ -401,9 +512,7 @@ static int xenXMConfigCacheRefresh (virConnectPtr conn) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
while ((ent = readdir(dh))) {
|
while ((ent = readdir(dh))) {
|
||||||
xenXMConfCachePtr entry;
|
|
||||||
struct stat st;
|
struct stat st;
|
||||||
int newborn = 0;
|
|
||||||
char path[PATH_MAX];
|
char path[PATH_MAX];
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -447,62 +556,8 @@ static int xenXMConfigCacheRefresh (virConnectPtr conn) {
|
|||||||
|
|
||||||
/* If we already have a matching entry and it is not
|
/* If we already have a matching entry and it is not
|
||||||
modified, then carry on to next one*/
|
modified, then carry on to next one*/
|
||||||
if ((entry = virHashLookup(configCache, path))) {
|
if (xenXMConfigCacheAddFile(conn, path) < 0) {
|
||||||
char *nameowner;
|
/* Ignoring errors, since alot of stuff goes wrong in /etc/xen */
|
||||||
|
|
||||||
if (entry->refreshedAt >= st.st_mtime) {
|
|
||||||
entry->refreshedAt = now;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* If we currently own the name, then release it and
|
|
||||||
re-acquire it later - just in case it was renamed */
|
|
||||||
nameowner = (char *)virHashLookup(nameConfigMap, entry->def->name);
|
|
||||||
if (nameowner && STREQ(nameowner, path)) {
|
|
||||||
virHashRemoveEntry(nameConfigMap, entry->def->name, NULL);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Clear existing config entry which needs refresh */
|
|
||||||
virDomainDefFree(entry->def);
|
|
||||||
entry->def = NULL;
|
|
||||||
} else { /* Completely new entry */
|
|
||||||
newborn = 1;
|
|
||||||
if (VIR_ALLOC(entry) < 0) {
|
|
||||||
xenXMError (conn, VIR_ERR_NO_MEMORY, "%s", strerror(errno));
|
|
||||||
goto cleanup;
|
|
||||||
}
|
|
||||||
memcpy(entry->filename, path, PATH_MAX);
|
|
||||||
}
|
|
||||||
entry->refreshedAt = now;
|
|
||||||
|
|
||||||
if (!(entry->def = xenXMConfigReadFile(conn, entry->filename))) {
|
|
||||||
if (!newborn)
|
|
||||||
virHashRemoveEntry(configCache, path, NULL);
|
|
||||||
VIR_FREE(entry);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* If its a completely new entry, it must be stuck into
|
|
||||||
the cache (refresh'd entries are already registered) */
|
|
||||||
if (newborn) {
|
|
||||||
if (virHashAddEntry(configCache, entry->filename, entry) < 0) {
|
|
||||||
virDomainDefFree(entry->def);
|
|
||||||
VIR_FREE(entry);
|
|
||||||
xenXMError (conn, VIR_ERR_INTERNAL_ERROR,
|
|
||||||
"%s", _("xenXMConfigCacheRefresh: virHashAddEntry"));
|
|
||||||
goto cleanup;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* See if we need to map this config file in as the primary owner
|
|
||||||
* of the domain in question
|
|
||||||
*/
|
|
||||||
if (!virHashLookup(nameConfigMap, entry->def->name)) {
|
|
||||||
if (virHashAddEntry(nameConfigMap, entry->def->name, entry->filename) < 0) {
|
|
||||||
virHashRemoveEntry(configCache, ent->d_name, NULL);
|
|
||||||
virDomainDefFree(entry->def);
|
|
||||||
VIR_FREE(entry);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -513,7 +568,6 @@ static int xenXMConfigCacheRefresh (virConnectPtr conn) {
|
|||||||
virHashRemoveSet(configCache, xenXMConfigReaper, xenXMConfigFree, (const void*) &now);
|
virHashRemoveSet(configCache, xenXMConfigReaper, xenXMConfigFree, (const void*) &now);
|
||||||
ret = 0;
|
ret = 0;
|
||||||
|
|
||||||
cleanup:
|
|
||||||
if (dh)
|
if (dh)
|
||||||
closedir(dh);
|
closedir(dh);
|
||||||
|
|
||||||
@ -1503,8 +1557,10 @@ virDomainPtr xenXMDomainLookupByName(virConnectPtr conn, const char *domname) {
|
|||||||
return (NULL);
|
return (NULL);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#ifndef WITH_XEN_INOTIFY
|
||||||
if (xenXMConfigCacheRefresh (conn) < 0)
|
if (xenXMConfigCacheRefresh (conn) < 0)
|
||||||
return (NULL);
|
return (NULL);
|
||||||
|
#endif
|
||||||
|
|
||||||
if (!(filename = virHashLookup(nameConfigMap, domname)))
|
if (!(filename = virHashLookup(nameConfigMap, domname)))
|
||||||
return (NULL);
|
return (NULL);
|
||||||
@ -1555,8 +1611,10 @@ virDomainPtr xenXMDomainLookupByUUID(virConnectPtr conn,
|
|||||||
return (NULL);
|
return (NULL);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#ifndef WITH_XEN_INOTIFY
|
||||||
if (xenXMConfigCacheRefresh (conn) < 0)
|
if (xenXMConfigCacheRefresh (conn) < 0)
|
||||||
return (NULL);
|
return (NULL);
|
||||||
|
#endif
|
||||||
|
|
||||||
if (!(entry = virHashSearch(configCache, xenXMDomainSearchForUUID, (const void *)uuid))) {
|
if (!(entry = virHashSearch(configCache, xenXMDomainSearchForUUID, (const void *)uuid))) {
|
||||||
return (NULL);
|
return (NULL);
|
||||||
@ -2208,8 +2266,10 @@ virDomainPtr xenXMDomainDefineXML(virConnectPtr conn, const char *xml) {
|
|||||||
if (conn->flags & VIR_CONNECT_RO)
|
if (conn->flags & VIR_CONNECT_RO)
|
||||||
return (NULL);
|
return (NULL);
|
||||||
|
|
||||||
|
#ifndef WITH_XEN_INOTIFY
|
||||||
if (xenXMConfigCacheRefresh (conn) < 0)
|
if (xenXMConfigCacheRefresh (conn) < 0)
|
||||||
return (NULL);
|
return (NULL);
|
||||||
|
#endif
|
||||||
|
|
||||||
if (!(def = virDomainDefParseString(conn, priv->caps, xml)))
|
if (!(def = virDomainDefParseString(conn, priv->caps, xml)))
|
||||||
return (NULL);
|
return (NULL);
|
||||||
@ -2376,8 +2436,10 @@ int xenXMListDefinedDomains(virConnectPtr conn, char **const names, int maxnames
|
|||||||
return (-1);
|
return (-1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#ifndef WITH_XEN_INOTIFY
|
||||||
if (xenXMConfigCacheRefresh (conn) < 0)
|
if (xenXMConfigCacheRefresh (conn) < 0)
|
||||||
return (-1);
|
return (-1);
|
||||||
|
#endif
|
||||||
|
|
||||||
if (maxnames > virHashSize(configCache))
|
if (maxnames > virHashSize(configCache))
|
||||||
maxnames = virHashSize(configCache);
|
maxnames = virHashSize(configCache);
|
||||||
@ -2401,8 +2463,10 @@ int xenXMNumOfDefinedDomains(virConnectPtr conn) {
|
|||||||
return (-1);
|
return (-1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#ifndef WITH_XEN_INOTIFY
|
||||||
if (xenXMConfigCacheRefresh (conn) < 0)
|
if (xenXMConfigCacheRefresh (conn) < 0)
|
||||||
return (-1);
|
return (-1);
|
||||||
|
#endif
|
||||||
|
|
||||||
return virHashSize(nameConfigMap);
|
return virHashSize(nameConfigMap);
|
||||||
}
|
}
|
||||||
|
@ -32,6 +32,12 @@
|
|||||||
extern struct xenUnifiedDriver xenXMDriver;
|
extern struct xenUnifiedDriver xenXMDriver;
|
||||||
int xenXMInit (void);
|
int xenXMInit (void);
|
||||||
|
|
||||||
|
virHashTablePtr xenXMGetConfigCache(void);
|
||||||
|
char *xenXMGetConfigDir(void);
|
||||||
|
int xenXMConfigCacheRefresh (virConnectPtr conn);
|
||||||
|
int xenXMConfigCacheAddFile(virConnectPtr conn, const char *filename);
|
||||||
|
int xenXMConfigCacheRemoveFile(virConnectPtr conn, const char *filename);
|
||||||
|
|
||||||
int xenXMOpen(virConnectPtr conn, virConnectAuthPtr auth, int flags);
|
int xenXMOpen(virConnectPtr conn, virConnectAuthPtr auth, int flags);
|
||||||
int xenXMClose(virConnectPtr conn);
|
int xenXMClose(virConnectPtr conn);
|
||||||
const char *xenXMGetType(virConnectPtr conn);
|
const char *xenXMGetType(virConnectPtr conn);
|
||||||
|
@ -29,6 +29,10 @@
|
|||||||
#include "virterror_internal.h"
|
#include "virterror_internal.h"
|
||||||
#include "datatypes.h"
|
#include "datatypes.h"
|
||||||
#include "driver.h"
|
#include "driver.h"
|
||||||
|
#include "memory.h"
|
||||||
|
#include "event.h"
|
||||||
|
#include "logging.h"
|
||||||
|
#include "uuid.h"
|
||||||
#include "xen_unified.h"
|
#include "xen_unified.h"
|
||||||
#include "xs_internal.h"
|
#include "xs_internal.h"
|
||||||
#include "xen_internal.h" /* for xenHypervisorCheckID */
|
#include "xen_internal.h" /* for xenHypervisorCheckID */
|
||||||
@ -42,6 +46,9 @@
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifndef PROXY
|
#ifndef PROXY
|
||||||
|
/* A list of active domain name/uuids */
|
||||||
|
static xenUnifiedDomainInfoListPtr activeDomainList = NULL;
|
||||||
|
|
||||||
static char *xenStoreDomainGetOSType(virDomainPtr domain);
|
static char *xenStoreDomainGetOSType(virDomainPtr domain);
|
||||||
|
|
||||||
struct xenUnifiedDriver xenStoreDriver = {
|
struct xenUnifiedDriver xenStoreDriver = {
|
||||||
@ -302,7 +309,52 @@ xenStoreOpen(virConnectPtr conn,
|
|||||||
}
|
}
|
||||||
return (-1);
|
return (-1);
|
||||||
}
|
}
|
||||||
return (0);
|
|
||||||
|
#ifndef PROXY
|
||||||
|
/* Init activeDomainList */
|
||||||
|
if ( VIR_ALLOC(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;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -324,10 +376,29 @@ xenStoreClose(virConnectPtr conn)
|
|||||||
}
|
}
|
||||||
|
|
||||||
priv = (xenUnifiedPrivatePtr) conn->privateData;
|
priv = (xenUnifiedPrivatePtr) conn->privateData;
|
||||||
|
|
||||||
|
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);
|
||||||
|
#ifndef PROXY
|
||||||
|
xenUnifiedDomainInfoListFree(activeDomainList);
|
||||||
|
#endif
|
||||||
if (priv->xshandle == NULL)
|
if (priv->xshandle == NULL)
|
||||||
return(-1);
|
return(-1);
|
||||||
|
|
||||||
|
if (priv->xsWatch != -1)
|
||||||
|
virEventRemoveHandle(priv->xsWatch);
|
||||||
xs_daemon_close(priv->xshandle);
|
xs_daemon_close(priv->xshandle);
|
||||||
|
priv->xshandle = NULL;
|
||||||
|
|
||||||
return (0);
|
return (0);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -920,3 +991,343 @@ char *xenStoreDomainGetName(virConnectPtr conn,
|
|||||||
return xs_read(priv->xshandle, 0, prop, &len);
|
return xs_read(priv->xshandle, 0, prop, &len);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#ifndef PROXY
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
#endif //PROXY
|
||||||
|
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
|
||||||
|
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++;
|
||||||
|
|
||||||
|
conn->refs++;
|
||||||
|
|
||||||
|
return xs_watch(priv->xshandle, watch->path, watch->token);
|
||||||
|
}
|
||||||
|
|
||||||
|
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]->path))
|
||||||
|
{
|
||||||
|
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--;
|
||||||
|
#ifndef PROXY
|
||||||
|
virUnrefConnect(conn);
|
||||||
|
#endif
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
void xenStoreWatchEvent(int watch ATTRIBUTE_UNUSED,
|
||||||
|
int fd ATTRIBUTE_UNUSED,
|
||||||
|
int events ATTRIBUTE_UNUSED,
|
||||||
|
void *data)
|
||||||
|
{
|
||||||
|
char **event;
|
||||||
|
char *path;
|
||||||
|
char *token;
|
||||||
|
unsigned int stringCount;
|
||||||
|
xenStoreWatchPtr sw;
|
||||||
|
|
||||||
|
virConnectPtr conn = data;
|
||||||
|
xenUnifiedPrivatePtr priv = (xenUnifiedPrivatePtr) conn->privateData;
|
||||||
|
if(!priv) return;
|
||||||
|
if(!priv->xshandle) return;
|
||||||
|
|
||||||
|
event = xs_read_watch(priv->xshandle, &stringCount);
|
||||||
|
if (!event)
|
||||||
|
return;
|
||||||
|
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifndef PROXY
|
||||||
|
/* The domain callback for the @introduceDomain watch */
|
||||||
|
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 = (xenUnifiedPrivatePtr) opaque;
|
||||||
|
|
||||||
|
retry:
|
||||||
|
new_domain_cnt = xenStoreNumOfDomains(conn);
|
||||||
|
if( VIR_ALLOC_N(new_domids,new_domain_cnt) < 0 ) {
|
||||||
|
virXenStoreError(NULL, VIR_ERR_NO_MEMORY,
|
||||||
|
"%s", _("failed to allocate domids"));
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
nread = xenStoreListDomains(conn, 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 < activeDomainList->count ; j++) {
|
||||||
|
if (activeDomainList->doms[j]->id == new_domids[i]) {
|
||||||
|
found = 1;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!found) {
|
||||||
|
virDomainPtr dom;
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
dom = virGetDomain(conn, name, uuid);
|
||||||
|
if (dom) {
|
||||||
|
dom->id = new_domids[i];
|
||||||
|
|
||||||
|
/* This domain was not in the old list. Emit an event */
|
||||||
|
xenUnifiedDomainEventDispatch(priv, dom,
|
||||||
|
VIR_DOMAIN_EVENT_STARTED,
|
||||||
|
VIR_DOMAIN_EVENT_STARTED_BOOTED);
|
||||||
|
|
||||||
|
/* Add to the list */
|
||||||
|
xenUnifiedAddDomainInfo(activeDomainList,
|
||||||
|
new_domids[i], name, uuid);
|
||||||
|
|
||||||
|
virUnrefDomain(dom);
|
||||||
|
}
|
||||||
|
|
||||||
|
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 */
|
||||||
|
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(!activeDomainList->count) return 0;
|
||||||
|
|
||||||
|
retry:
|
||||||
|
new_domain_cnt = xenStoreNumOfDomains(conn);
|
||||||
|
|
||||||
|
if( VIR_ALLOC_N(new_domids,new_domain_cnt) < 0 ) {
|
||||||
|
virXenStoreError(NULL, VIR_ERR_NO_MEMORY,
|
||||||
|
"%s", _("failed to allocate domids"));
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
nread = xenStoreListDomains(conn, 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 < activeDomainList->count ; j++) {
|
||||||
|
found = 0;
|
||||||
|
for (i=0 ; i < new_domain_cnt ; i++) {
|
||||||
|
if (activeDomainList->doms[j]->id == new_domids[i]) {
|
||||||
|
found = 1;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!found) {
|
||||||
|
virDomainPtr dom = virGetDomain(conn,
|
||||||
|
activeDomainList->doms[j]->name,
|
||||||
|
activeDomainList->doms[j]->uuid);
|
||||||
|
if(dom) {
|
||||||
|
dom->id = -1;
|
||||||
|
/* This domain was not in the new list. Emit an event */
|
||||||
|
xenUnifiedDomainEventDispatch(priv, dom,
|
||||||
|
VIR_DOMAIN_EVENT_STOPPED,
|
||||||
|
VIR_DOMAIN_EVENT_STOPPED_SHUTDOWN);
|
||||||
|
/* Remove from the list */
|
||||||
|
xenUnifiedRemoveDomainInfo(activeDomainList,
|
||||||
|
activeDomainList->doms[j]->id,
|
||||||
|
activeDomainList->doms[j]->name,
|
||||||
|
activeDomainList->doms[j]->uuid);
|
||||||
|
|
||||||
|
virUnrefDomain(dom);
|
||||||
|
removed = 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
VIR_FREE(new_domids);
|
||||||
|
|
||||||
|
if (!removed && retries--) {
|
||||||
|
DEBUG0("No domains removed, retrying");
|
||||||
|
usleep(100 * 1000);
|
||||||
|
goto retry;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif //PROXY
|
||||||
|
@ -51,5 +51,57 @@ char * xenStoreDomainGetDiskID(virConnectPtr conn,
|
|||||||
const char *dev);
|
const char *dev);
|
||||||
char * xenStoreDomainGetName(virConnectPtr conn,
|
char * xenStoreDomainGetName(virConnectPtr conn,
|
||||||
int id);
|
int id);
|
||||||
|
int xenStoreDomainGetUUID(virConnectPtr conn,
|
||||||
|
int id,
|
||||||
|
unsigned char *uuid);
|
||||||
|
|
||||||
|
typedef int (*xenStoreWatchCallback)(virConnectPtr conn,
|
||||||
|
const char *path,
|
||||||
|
const char *token,
|
||||||
|
void *opaque);
|
||||||
|
|
||||||
|
struct _xenStoreWatch {
|
||||||
|
char *path;
|
||||||
|
char *token;
|
||||||
|
xenStoreWatchCallback cb;
|
||||||
|
void *opaque;
|
||||||
|
};
|
||||||
|
typedef struct _xenStoreWatch xenStoreWatch;
|
||||||
|
typedef xenStoreWatch *xenStoreWatchPtr;
|
||||||
|
|
||||||
|
struct _xenStoreWatchList {
|
||||||
|
unsigned int count;
|
||||||
|
xenStoreWatchPtr *watches;
|
||||||
|
};
|
||||||
|
typedef struct _xenStoreWatchList xenStoreWatchList;
|
||||||
|
typedef xenStoreWatchList *xenStoreWatchListPtr;
|
||||||
|
|
||||||
|
|
||||||
|
void xenStoreWatchListFree(xenStoreWatchListPtr head);
|
||||||
|
|
||||||
|
int xenStoreAddWatch(virConnectPtr conn,
|
||||||
|
const char *path,
|
||||||
|
const char *token,
|
||||||
|
xenStoreWatchCallback cb,
|
||||||
|
void *opaque);
|
||||||
|
int xenStoreRemoveWatch(virConnectPtr conn,
|
||||||
|
const char *path,
|
||||||
|
const char *token);
|
||||||
|
xenStoreWatchPtr xenStoreFindWatch(xenStoreWatchListPtr list,
|
||||||
|
const char *path,
|
||||||
|
const char *token);
|
||||||
|
|
||||||
|
void xenStoreWatchEvent(int watch, int fd, int events, void *data);
|
||||||
|
|
||||||
|
/* domain events */
|
||||||
|
int xenStoreDomainIntroduced(virConnectPtr conn,
|
||||||
|
const char *path,
|
||||||
|
const char *token,
|
||||||
|
void *opaque);
|
||||||
|
int xenStoreDomainReleased(virConnectPtr conn,
|
||||||
|
const char *path,
|
||||||
|
const char *token,
|
||||||
|
void *opaque);
|
||||||
|
|
||||||
|
int xenStoreDomainEventEmitted(virDomainEventType evt);
|
||||||
#endif /* __VIR_XS_INTERNAL_H__ */
|
#endif /* __VIR_XS_INTERNAL_H__ */
|
||||||
|
Loading…
Reference in New Issue
Block a user