/* * xs_internal.c: access to Xen Store * * Copyright (C) 2006, 2009-2012 Red Hat, Inc. * * 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, see * . * * Daniel Veillard */ #include #include #include #include #include #include #include #include #include #include #include #if HAVE_XENSTORE_H # include #else # include #endif #include "virterror_internal.h" #include "datatypes.h" #include "driver.h" #include "viralloc.h" #include "virlog.h" #include "uuid.h" #include "xen_driver.h" #include "xs_internal.h" #include "xen_hypervisor.h" #define VIR_FROM_THIS VIR_FROM_XEN static char *xenStoreDomainGetOSType(virDomainPtr domain); static void xenStoreWatchEvent(int watch, int fd, int events, void *data); static void xenStoreWatchListFree(xenStoreWatchListPtr list); struct xenUnifiedDriver xenStoreDriver = { .xenClose = xenStoreClose, .xenDomainShutdown = xenStoreDomainShutdown, .xenDomainReboot = xenStoreDomainReboot, .xenDomainGetOSType = xenStoreDomainGetOSType, .xenDomainGetMaxMemory = xenStoreDomainGetMaxMemory, .xenDomainSetMemory = xenStoreDomainSetMemory, .xenDomainGetInfo = xenStoreGetDomainInfo, }; /************************************************************************ * * * Helper internal APIs * * * ************************************************************************/ /** * 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); } /** * 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); } /** * 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; } /************************************************************************ * * * 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, unsigned int flags) { xenUnifiedPrivatePtr priv = (xenUnifiedPrivatePtr) conn->privateData; virCheckFlags(VIR_CONNECT_RO, VIR_DRV_OPEN_ERROR); if (flags & VIR_CONNECT_RO) priv->xshandle = xs_daemon_open_readonly(); else priv->xshandle = xs_daemon_open(); 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()) { virReportError(VIR_ERR_NO_XEN, "%s", _("failed to connect to Xen Store")); } return -1; } /* Init activeDomainList */ if (VIR_ALLOC(priv->activeDomainList) < 0) { virReportOOMError(); 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) { virReportOOMError(); return -1; } /* This will get called once at start */ if (xenStoreAddWatch(conn, "@releaseDomain", "releaseDomain", xenStoreDomainReleased, priv) < 0) { virReportError(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) { virReportError(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) VIR_DEBUG("Failed to add event handle, disabling events"); 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) { virReportError(VIR_ERR_INVALID_ARG, __FUNCTION__); return -1; } priv = (xenUnifiedPrivatePtr) conn->privateData; if (xenStoreRemoveWatch(conn, "@introduceDomain", "introduceDomain") < 0) { VIR_DEBUG("Warning, could not remove @introduceDomain watch"); /* not fatal */ } if (xenStoreRemoveWatch(conn, "@releaseDomain", "releaseDomain") < 0) { VIR_DEBUG("Warning, could not remove @releaseDomain watch"); /* not fatal */ } xenStoreWatchListFree(priv->xsWatchList); priv->xsWatchList = NULL; xenUnifiedDomainInfoListFree(priv->activeDomainList); priv->activeDomainList = NULL; if (priv->xshandle == NULL) return -1; if (priv->xsWatch != -1) virEventRemoveHandle(priv->xsWatch); xs_daemon_close(priv->xshandle); priv->xshandle = NULL; return 0; } /** * xenStoreGetDomainInfo: * @domain: pointer to the domain block * @info: the place where information should be stored * * Do a 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)) { virReportError(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; VIR_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); VIR_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); VIR_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; VIR_FREE(tmp2); } return 0; } /** * xenStoreDomainGetState: * @domain: pointer to the domain block * @state: returned domain's state * @reason: returned state reason * @flags: additional flags, 0 for now * * Returns 0 in case of success, -1 in case of error. */ int xenStoreDomainGetState(virDomainPtr domain, int *state, int *reason, unsigned int flags) { char *running; virCheckFlags(0, -1); if (domain->id == -1) return -1; running = virDomainDoStoreQuery(domain->conn, domain->id, "running"); if (running && *running == '1') *state = VIR_DOMAIN_RUNNING; else *state = VIR_DOMAIN_NOSTATE; if (reason) *reason = 0; VIR_FREE(running); 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)) { virReportError(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 long xenStoreDomainGetMaxMemory(virDomainPtr domain) { char *tmp; unsigned long long ret = 0; xenUnifiedPrivatePtr priv; if (!VIR_IS_CONNECTED_DOMAIN(domain)) return ret; if (domain->id == -1) return 0; priv = domain->conn->privateData; xenUnifiedLock(priv); tmp = virDomainDoStoreQuery(domain->conn, domain->id, "memory/target"); if (tmp != NULL) { ret = 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 = NULL, *endptr; int i, ret = -1, realnum = 0; long id; xenUnifiedPrivatePtr priv; if (conn == NULL) { virReportError(VIR_ERR_INVALID_ARG, __FUNCTION__); return -1; } priv = (xenUnifiedPrivatePtr) conn->privateData; if (priv->xshandle == NULL) { virReportError(VIR_ERR_INVALID_ARG, __FUNCTION__); return -1; } idlist = xs_directory(priv->xshandle, 0, "/local/domain", &num); if (idlist) { for (i = 0; i < num; i++) { id = strtol(idlist[i], &endptr, 10); if ((endptr == idlist[i]) || (*endptr != 0)) goto out; /* Sometimes xenstore has stale domain IDs, so filter against the hypervisor's info */ if (xenHypervisorHasDomain(conn, (int)id)) realnum++; } out: VIR_FREE(idlist); ret = realnum; } 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(virConnectPtr conn, 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; /* Sometimes xenstore has stale domain IDs, so filter against the hypervisor's info */ if (xenHypervisorHasDomain(conn, (int)id)) 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)) { virReportError(VIR_ERR_INVALID_ARG, __FUNCTION__); return -1; } priv = (xenUnifiedPrivatePtr) conn->privateData; xenUnifiedLock(priv); ret = xenStoreDoListDomains(conn, 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)) { virReportError(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); VIR_FREE(tmp); if (found) break; } } if (!found) goto done; ret = virGetDomain(conn, name, NULL); if (ret == NULL) goto done; ret->id = id; done: VIR_FREE(xenddomain); VIR_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)) { virReportError(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) { int ret; xenUnifiedPrivatePtr priv; virCheckFlags(0, -1); if ((domain == NULL) || (domain->conn == NULL)) { virReportError(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)) { virReportError(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; } /** * 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; VIR_FREE(tmp); } return ret; } /** * xenStoreDomainGetConsolePath: * @conn: the hypervisor connection * @domid: id of the domain * * Return the path to the pseudo 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"); } /** * xenStoreDomainGetSerailConsolePath: * @conn: the hypervisor connection * @domid: id of the domain * * Return the path to the pseudo 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 * xenStoreDomainGetSerialConsolePath(virConnectPtr conn, int domid) { return virDomainDoStoreQuery(conn, domid, "serial/0/tty"); } /* * 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]); if (ret == NULL) virReportOOMError(); 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)) { VIR_FREE(val); } else { ret = strdup(list[i]); if (ret == NULL) virReportOOMError(); VIR_FREE(val); VIR_FREE(list); return ret; } } VIR_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)) { VIR_FREE(val); } else { ret = strdup(list[i]); if (ret == NULL) virReportOOMError(); VIR_FREE(val); VIR_FREE(list); return ret; } } VIR_FREE(list); } return NULL; } /* * xenStoreDomainGetPCIID: * @conn: pointer to the connection. * @id: the domain id * @bdf: the PCI BDF * * Get the reference (i.e. the string number) for the device on that domain * which uses the given PCI 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 * xenStoreDomainGetPCIID(virConnectPtr conn, int id, const char *bdf) { 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 (bdf == NULL) return NULL; snprintf(dir, sizeof(dir), "/local/domain/0/backend/pci/%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], "dev-0"); if ((val = xs_read(priv->xshandle, 0, path, &len)) == NULL) break; bool match = STREQ(val, bdf); VIR_FREE(val); if (match) { ret = strdup(list[i]); break; } } VIR_FREE(list); return ret; } /* * 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); } /* * 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); /* Strip optional version suffix when VM was renamed */ if (len > 40) /* strlen('/vm/') + VIR_UUID_STRING_BUFLEN - sizeof('\0') */ uuidstr[40] = '\0'; /* remove "/vm/" */ ret = virUUIDParse(uuidstr + 4, uuid); VIR_FREE(uuidstr); return ret; } static void xenStoreWatchListFree(xenStoreWatchListPtr list) { int i; for (i=0; icount; 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 = NULL; 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)) { virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("watch already tracked")); return -1; } } if (VIR_ALLOC(watch) < 0) goto no_memory; watch->path = strdup(path); watch->token = strdup(token); watch->cb = cb; watch->opaque = opaque; if (watch->path == NULL || watch->token == NULL) { goto no_memory; } /* Make space on list */ n = list->count; if (VIR_REALLOC_N(list->watches, n + 1) < 0) { goto no_memory; } list->watches[n] = watch; list->count++; return xs_watch(priv->xshandle, watch->path, watch->token); no_memory: if (watch) { VIR_FREE(watch->path); VIR_FREE(watch->token); VIR_FREE(watch); } virReportOOMError(); return -1; } /* * 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)) { VIR_DEBUG("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 (new_domain_cnt < 0) return -1; if (VIR_ALLOC_N(new_domids,new_domain_cnt) < 0) { virReportOOMError(); return -1; } nread = xenStoreDoListDomains(conn, 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--) { VIR_DEBUG("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 (new_domain_cnt < 0) return -1; if (VIR_ALLOC_N(new_domids,new_domain_cnt) < 0) { virReportOOMError(); return -1; } nread = xenStoreDoListDomains(conn, 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--) { VIR_DEBUG("No domains removed, retrying"); usleep(100 * 1000); goto retry; } return 0; }