[libvirt] [PATCH 10/16] Move LXC process management code into separate file
Daniel Veillard
veillard at redhat.com
Thu Jul 19 08:48:49 UTC 2012
On Wed, Jul 18, 2012 at 05:32:31PM +0100, Daniel P. Berrange wrote:
> From: "Daniel P. Berrange" <berrange at redhat.com>
>
> Move all the code that manages stop/start of LXC processes
> into separate lxc_process.{c,h} file to make the lxc_driver.c
> file smaller
>
> Signed-off-by: Daniel P. Berrange <berrange at redhat.com>
> ---
> po/POTFILES.in | 1 +
> src/Makefile.am | 1 +
> src/lxc/lxc_conf.h | 10 +
> src/lxc/lxc_driver.c | 1236 +-----------------------------------------------
> src/lxc/lxc_process.c | 1242 +++++++++++++++++++++++++++++++++++++++++++++++++
> src/lxc/lxc_process.h | 49 ++
> 6 files changed, 1313 insertions(+), 1226 deletions(-)
> create mode 100644 src/lxc/lxc_process.c
> create mode 100644 src/lxc/lxc_process.h
>
> diff --git a/po/POTFILES.in b/po/POTFILES.in
> index 2d5735a..dc46941 100644
> --- a/po/POTFILES.in
> +++ b/po/POTFILES.in
> @@ -47,6 +47,7 @@ src/lxc/lxc_container.c
> src/lxc/lxc_conf.c
> src/lxc/lxc_controller.c
> src/lxc/lxc_driver.c
> +src/lxc/lxc_process.c
> src/libxl/libxl_driver.c
> src/libxl/libxl_conf.c
> src/network/bridge_driver.c
> diff --git a/src/Makefile.am b/src/Makefile.am
> index 9e16d06..59f1ac8 100644
> --- a/src/Makefile.am
> +++ b/src/Makefile.am
> @@ -352,6 +352,7 @@ LXC_DRIVER_SOURCES = \
> lxc/lxc_container.c lxc/lxc_container.h \
> lxc/lxc_cgroup.c lxc/lxc_cgroup.h \
> lxc/lxc_domain.c lxc/lxc_domain.h \
> + lxc/lxc_process.c lxc/lxc_process.h \
> lxc/lxc_driver.c lxc/lxc_driver.h
>
> LXC_CONTROLLER_SOURCES = \
> diff --git a/src/lxc/lxc_conf.h b/src/lxc/lxc_conf.h
> index cc279b2..937da16 100644
> --- a/src/lxc/lxc_conf.h
> +++ b/src/lxc/lxc_conf.h
> @@ -78,4 +78,14 @@ virCapsPtr lxcCapsInit(lxc_driver_t *driver);
> virReportErrorHelper(VIR_FROM_LXC, code, __FILE__, \
> __FUNCTION__, __LINE__, __VA_ARGS__)
>
> +static inline void lxcDriverLock(lxc_driver_t *driver)
> +{
> + virMutexLock(&driver->lock);
> +}
> +static inline void lxcDriverUnlock(lxc_driver_t *driver)
> +{
> + virMutexUnlock(&driver->lock);
> +}
> +
> +
> #endif /* LXC_CONF_H */
> diff --git a/src/lxc/lxc_driver.c b/src/lxc/lxc_driver.c
> index d3895d5..d7f052f 100644
> --- a/src/lxc/lxc_driver.c
> +++ b/src/lxc/lxc_driver.c
> @@ -42,6 +42,7 @@
> #include "lxc_container.h"
> #include "lxc_domain.h"
> #include "lxc_driver.h"
> +#include "lxc_process.h"
> #include "memory.h"
> #include "util.h"
> #include "virnetdevbridge.h"
> @@ -66,7 +67,6 @@
>
> #define VIR_FROM_THIS VIR_FROM_LXC
>
> -#define START_POSTFIX ": starting up\n"
>
> #define LXC_NB_MEM_PARAM 3
>
> @@ -76,31 +76,6 @@ static lxc_driver_t *lxc_driver = NULL;
>
> /* Functions */
>
> -static void lxcDriverLock(lxc_driver_t *driver)
> -{
> - virMutexLock(&driver->lock);
> -}
> -static void lxcDriverUnlock(lxc_driver_t *driver)
> -{
> - virMutexUnlock(&driver->lock);
> -}
> -
> -static void lxcDomainEventQueue(lxc_driver_t *driver,
> - virDomainEventPtr event);
> -
> -static int lxcVmTerminate(lxc_driver_t *driver,
> - virDomainObjPtr vm,
> - virDomainShutoffReason reason);
> -static int lxcProcessAutoDestroyInit(lxc_driver_t *driver);
> -static void lxcProcessAutoDestroyRun(lxc_driver_t *driver,
> - virConnectPtr conn);
> -static void lxcProcessAutoDestroyShutdown(lxc_driver_t *driver);
> -static int lxcProcessAutoDestroyAdd(lxc_driver_t *driver,
> - virDomainObjPtr vm,
> - virConnectPtr conn);
> -static int lxcProcessAutoDestroyRemove(lxc_driver_t *driver,
> - virDomainObjPtr vm);
> -
>
> static virDrvOpenStatus lxcOpen(virConnectPtr conn,
> virConnectAuthPtr auth ATTRIBUTE_UNUSED,
> @@ -451,7 +426,7 @@ cleanup:
> if (vm)
> virDomainObjUnlock(vm);
> if (event)
> - lxcDomainEventQueue(driver, event);
> + virDomainEventStateQueue(driver->domainEventState, event);
> lxcDriverUnlock(driver);
> return dom;
> }
> @@ -504,7 +479,7 @@ cleanup:
> if (vm)
> virDomainObjUnlock(vm);
> if (event)
> - lxcDomainEventQueue(driver, event);
> + virDomainEventStateQueue(driver->domainEventState, event);
> lxcDriverUnlock(driver);
> return ret;
> }
> @@ -963,1075 +938,6 @@ cleanup:
> }
>
>
> -static int lxcProcessAutoDestroyInit(lxc_driver_t *driver)
> -{
> - if (!(driver->autodestroy = virHashCreate(5, NULL)))
> - return -1;
> -
> - return 0;
> -}
> -
> -struct lxcProcessAutoDestroyData {
> - lxc_driver_t *driver;
> - virConnectPtr conn;
> -};
> -
> -static void lxcProcessAutoDestroyDom(void *payload,
> - const void *name,
> - void *opaque)
> -{
> - struct lxcProcessAutoDestroyData *data = opaque;
> - virConnectPtr conn = payload;
> - const char *uuidstr = name;
> - unsigned char uuid[VIR_UUID_BUFLEN];
> - virDomainObjPtr dom;
> - virDomainEventPtr event = NULL;
> -
> - VIR_DEBUG("conn=%p uuidstr=%s thisconn=%p", conn, uuidstr, data->conn);
> -
> - if (data->conn != conn)
> - return;
> -
> - if (virUUIDParse(uuidstr, uuid) < 0) {
> - VIR_WARN("Failed to parse %s", uuidstr);
> - return;
> - }
> -
> - if (!(dom = virDomainFindByUUID(&data->driver->domains,
> - uuid))) {
> - VIR_DEBUG("No domain object to kill");
> - return;
> - }
> -
> - VIR_DEBUG("Killing domain");
> - lxcVmTerminate(data->driver, dom, VIR_DOMAIN_SHUTOFF_DESTROYED);
> - virDomainAuditStop(dom, "destroyed");
> - event = virDomainEventNewFromObj(dom,
> - VIR_DOMAIN_EVENT_STOPPED,
> - VIR_DOMAIN_EVENT_STOPPED_DESTROYED);
> -
> - if (dom && !dom->persistent)
> - virDomainRemoveInactive(&data->driver->domains, dom);
> -
> - if (dom)
> - virDomainObjUnlock(dom);
> - if (event)
> - lxcDomainEventQueue(data->driver, event);
> - virHashRemoveEntry(data->driver->autodestroy, uuidstr);
> -}
> -
> -/*
> - * Precondition: driver is locked
> - */
> -static void lxcProcessAutoDestroyRun(lxc_driver_t *driver, virConnectPtr conn)
> -{
> - struct lxcProcessAutoDestroyData data = {
> - driver, conn
> - };
> - VIR_DEBUG("conn=%p", conn);
> - virHashForEach(driver->autodestroy, lxcProcessAutoDestroyDom, &data);
> -}
> -
> -static void lxcProcessAutoDestroyShutdown(lxc_driver_t *driver)
> -{
> - virHashFree(driver->autodestroy);
> -}
> -
> -static int lxcProcessAutoDestroyAdd(lxc_driver_t *driver,
> - virDomainObjPtr vm,
> - virConnectPtr conn)
> -{
> - char uuidstr[VIR_UUID_STRING_BUFLEN];
> - virUUIDFormat(vm->def->uuid, uuidstr);
> - VIR_DEBUG("vm=%s uuid=%s conn=%p", vm->def->name, uuidstr, conn);
> - if (virHashAddEntry(driver->autodestroy, uuidstr, conn) < 0)
> - return -1;
> - return 0;
> -}
> -
> -static int lxcProcessAutoDestroyRemove(lxc_driver_t *driver,
> - virDomainObjPtr vm)
> -{
> - char uuidstr[VIR_UUID_STRING_BUFLEN];
> - virUUIDFormat(vm->def->uuid, uuidstr);
> - VIR_DEBUG("vm=%s uuid=%s", vm->def->name, uuidstr);
> - if (virHashRemoveEntry(driver->autodestroy, uuidstr) < 0)
> - return -1;
> - return 0;
> -}
> -
> -
> -/**
> - * lxcVmCleanup:
> - * @driver: pointer to driver structure
> - * @vm: pointer to VM to clean up
> - * @reason: reason for switching the VM to shutoff state
> - *
> - * Cleanout resources associated with the now dead VM
> - *
> - */
> -static void lxcVmCleanup(lxc_driver_t *driver,
> - virDomainObjPtr vm,
> - virDomainShutoffReason reason)
> -{
> - virCgroupPtr cgroup;
> - int i;
> - lxcDomainObjPrivatePtr priv = vm->privateData;
> - virNetDevVPortProfilePtr vport = NULL;
> -
> - /* now that we know it's stopped call the hook if present */
> - if (virHookPresent(VIR_HOOK_DRIVER_LXC)) {
> - char *xml = virDomainDefFormat(vm->def, 0);
> -
> - /* we can't stop the operation even if the script raised an error */
> - virHookCall(VIR_HOOK_DRIVER_LXC, vm->def->name,
> - VIR_HOOK_LXC_OP_STOPPED, VIR_HOOK_SUBOP_END,
> - NULL, xml, NULL);
> - VIR_FREE(xml);
> - }
> -
> - /* Stop autodestroy in case guest is restarted */
> - lxcProcessAutoDestroyRemove(driver, vm);
> -
> - virEventRemoveHandle(priv->monitorWatch);
> - VIR_FORCE_CLOSE(priv->monitor);
> -
> - virPidFileDelete(driver->stateDir, vm->def->name);
> - virDomainDeleteConfig(driver->stateDir, NULL, vm);
> -
> - virDomainObjSetState(vm, VIR_DOMAIN_SHUTOFF, reason);
> - vm->pid = -1;
> - vm->def->id = -1;
> - priv->monitor = -1;
> - priv->monitorWatch = -1;
> -
> - for (i = 0 ; i < vm->def->nnets ; i++) {
> - virDomainNetDefPtr iface = vm->def->nets[i];
> - vport = virDomainNetGetActualVirtPortProfile(iface);
> - ignore_value(virNetDevSetOnline(iface->ifname, false));
> - if (vport && vport->virtPortType == VIR_NETDEV_VPORT_PROFILE_OPENVSWITCH)
> - ignore_value(virNetDevOpenvswitchRemovePort(
> - virDomainNetGetActualBridgeName(iface),
> - iface->ifname));
> - ignore_value(virNetDevVethDelete(iface->ifname));
> - networkReleaseActualDevice(iface);
> - }
> -
> - virDomainConfVMNWFilterTeardown(vm);
> -
> - if (driver->cgroup &&
> - virCgroupForDomain(driver->cgroup, vm->def->name, &cgroup, 0) == 0) {
> - virCgroupRemove(cgroup);
> - virCgroupFree(&cgroup);
> - }
> -
> - /* now that we know it's stopped call the hook if present */
> - if (virHookPresent(VIR_HOOK_DRIVER_LXC)) {
> - char *xml = virDomainDefFormat(vm->def, 0);
> -
> - /* we can't stop the operation even if the script raised an error */
> - virHookCall(VIR_HOOK_DRIVER_LXC, vm->def->name,
> - VIR_HOOK_LXC_OP_RELEASE, VIR_HOOK_SUBOP_END,
> - NULL, xml, NULL);
> - VIR_FREE(xml);
> - }
> -
> - if (vm->newDef) {
> - virDomainDefFree(vm->def);
> - vm->def = vm->newDef;
> - vm->def->id = -1;
> - vm->newDef = NULL;
> - }
> -}
> -
> -
> -static int lxcSetupInterfaceBridged(virConnectPtr conn,
> - virDomainDefPtr vm,
> - virDomainNetDefPtr net,
> - const char *brname,
> - unsigned int *nveths,
> - char ***veths)
> -{
> - int ret = -1;
> - char *parentVeth;
> - char *containerVeth = NULL;
> - const virNetDevVPortProfilePtr vport = virDomainNetGetActualVirtPortProfile(net);
> -
> - VIR_DEBUG("calling vethCreate()");
> - parentVeth = net->ifname;
> - if (virNetDevVethCreate(&parentVeth, &containerVeth) < 0)
> - goto cleanup;
> - VIR_DEBUG("parentVeth: %s, containerVeth: %s", parentVeth, containerVeth);
> -
> - if (net->ifname == NULL)
> - net->ifname = parentVeth;
> -
> - if (VIR_REALLOC_N(*veths, (*nveths)+1) < 0) {
> - virReportOOMError();
> - VIR_FREE(containerVeth);
> - goto cleanup;
> - }
> - (*veths)[(*nveths)] = containerVeth;
> - (*nveths)++;
> -
> - if (virNetDevSetMAC(containerVeth, &net->mac) < 0)
> - goto cleanup;
> -
> - if (vport && vport->virtPortType == VIR_NETDEV_VPORT_PROFILE_OPENVSWITCH)
> - ret = virNetDevOpenvswitchAddPort(brname, parentVeth, &net->mac,
> - vm->uuid, vport);
> - else
> - ret = virNetDevBridgeAddPort(brname, parentVeth);
> - if (ret < 0)
> - goto cleanup;
> -
> - if (virNetDevSetOnline(parentVeth, true) < 0)
> - goto cleanup;
> -
> - if (virNetDevBandwidthSet(net->ifname,
> - virDomainNetGetActualBandwidth(net)) < 0) {
> - lxcError(VIR_ERR_INTERNAL_ERROR,
> - _("cannot set bandwidth limits on %s"),
> - net->ifname);
> - goto cleanup;
> - }
> -
> - if (net->filter &&
> - virDomainConfNWFilterInstantiate(conn, vm->uuid, net) < 0)
> - goto cleanup;
> -
> - ret = 0;
> -
> -cleanup:
> - return ret;
> -}
> -
> -
> -static int lxcSetupInterfaceDirect(virConnectPtr conn,
> - virDomainDefPtr def,
> - virDomainNetDefPtr net,
> - unsigned int *nveths,
> - char ***veths)
> -{
> - int ret = 0;
> - char *res_ifname = NULL;
> - lxc_driver_t *driver = conn->privateData;
> - virNetDevBandwidthPtr bw;
> - virNetDevVPortProfilePtr prof;
> -
> - /* XXX how todo bandwidth controls ?
> - * Since the 'net-ifname' is about to be moved to a different
> - * namespace & renamed, there will be no host side visible
> - * interface for the container to attach rules to
> - */
> - bw = virDomainNetGetActualBandwidth(net);
> - if (bw) {
> - lxcError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
> - _("Unable to set network bandwidth on direct interfaces"));
> - return -1;
> - }
> -
> - /* XXX how todo port profiles ?
> - * Although we can do the association during container
> - * startup, at shutdown we are unable to disassociate
> - * because the macvlan device was moved to the container
> - * and automagically dies when the container dies. So
> - * we have no dev to perform disassociation with.
> - */
> - prof = virDomainNetGetActualVirtPortProfile(net);
> - if (prof) {
> - lxcError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
> - _("Unable to set port profile on direct interfaces"));
> - return -1;
> - }
> -
> - if (VIR_REALLOC_N(*veths, (*nveths)+1) < 0) {
> - virReportOOMError();
> - return -1;
> - }
> - (*veths)[(*nveths)] = NULL;
> -
> - if (virNetDevMacVLanCreateWithVPortProfile(
> - net->ifname, &net->mac,
> - virDomainNetGetActualDirectDev(net),
> - virDomainNetGetActualDirectMode(net),
> - false, false, def->uuid,
> - virDomainNetGetActualVirtPortProfile(net),
> - &res_ifname,
> - VIR_NETDEV_VPORT_PROFILE_OP_CREATE,
> - driver->stateDir,
> - virDomainNetGetActualBandwidth(net)) < 0)
> - goto cleanup;
> -
> - (*veths)[(*nveths)] = res_ifname;
> - (*nveths)++;
> -
> - ret = 0;
> -
> -cleanup:
> - return ret;
> -}
> -
> -
> -/**
> - * lxcSetupInterfaces:
> - * @conn: pointer to connection
> - * @def: pointer to virtual machine structure
> - * @nveths: number of interfaces
> - * @veths: interface names
> - *
> - * Sets up the container interfaces by creating the veth device pairs and
> - * attaching the parent end to the appropriate bridge. The container end
> - * will moved into the container namespace later after clone has been called.
> - *
> - * Returns 0 on success or -1 in case of error
> - */
> -static int lxcSetupInterfaces(virConnectPtr conn,
> - virDomainDefPtr def,
> - unsigned int *nveths,
> - char ***veths)
> -{
> - int ret = -1;
> - size_t i;
> -
> - for (i = 0 ; i < def->nnets ; i++) {
> - /* If appropriate, grab a physical device from the configured
> - * network's pool of devices, or resolve bridge device name
> - * to the one defined in the network definition.
> - */
> - if (networkAllocateActualDevice(def->nets[i]) < 0)
> - goto cleanup;
> -
> - switch (virDomainNetGetActualType(def->nets[i])) {
> - case VIR_DOMAIN_NET_TYPE_NETWORK: {
> - virNetworkPtr network;
> - char *brname = NULL;
> -
> - if (!(network = virNetworkLookupByName(conn,
> - def->nets[i]->data.network.name)))
> - goto cleanup;
> -
> - brname = virNetworkGetBridgeName(network);
> - virNetworkFree(network);
> - if (!brname)
> - goto cleanup;
> -
> - if (lxcSetupInterfaceBridged(conn,
> - def,
> - def->nets[i],
> - brname,
> - nveths,
> - veths) < 0) {
> - VIR_FREE(brname);
> - goto cleanup;
> - }
> - VIR_FREE(brname);
> - break;
> - }
> - case VIR_DOMAIN_NET_TYPE_BRIDGE: {
> - const char *brname = virDomainNetGetActualBridgeName(def->nets[i]);
> - if (!brname) {
> - lxcError(VIR_ERR_INTERNAL_ERROR, "%s",
> - _("No bridge name specified"));
> - goto cleanup;
> - }
> - if (lxcSetupInterfaceBridged(conn,
> - def,
> - def->nets[i],
> - brname,
> - nveths,
> - veths) < 0)
> - goto cleanup;
> - } break;
> -
> - case VIR_DOMAIN_NET_TYPE_DIRECT:
> - if (lxcSetupInterfaceDirect(conn,
> - def,
> - def->nets[i],
> - nveths,
> - veths) < 0)
> - goto cleanup;
> - break;
> -
> - case VIR_DOMAIN_NET_TYPE_USER:
> - case VIR_DOMAIN_NET_TYPE_ETHERNET:
> - case VIR_DOMAIN_NET_TYPE_SERVER:
> - case VIR_DOMAIN_NET_TYPE_CLIENT:
> - case VIR_DOMAIN_NET_TYPE_MCAST:
> - case VIR_DOMAIN_NET_TYPE_INTERNAL:
> - case VIR_DOMAIN_NET_TYPE_LAST:
> - lxcError(VIR_ERR_INTERNAL_ERROR,
> - _("Unsupported network type %s"),
> - virDomainNetTypeToString(
> - virDomainNetGetActualType(def->nets[i])
> - ));
> - goto cleanup;
> - }
> - }
> -
> - ret= 0;
> -
> -cleanup:
> - if (ret != 0) {
> - for (i = 0 ; i < def->nnets ; i++) {
> - virDomainNetDefPtr iface = def->nets[i];
> - virNetDevVPortProfilePtr vport = virDomainNetGetActualVirtPortProfile(iface);
> - if (vport && vport->virtPortType == VIR_NETDEV_VPORT_PROFILE_OPENVSWITCH)
> - ignore_value(virNetDevOpenvswitchRemovePort(
> - virDomainNetGetActualBridgeName(iface),
> - iface->ifname));
> - networkReleaseActualDevice(iface);
> - }
> - }
> - return ret;
> -}
> -
> -
> -static int lxcMonitorClient(lxc_driver_t * driver,
> - virDomainObjPtr vm)
> -{
> - char *sockpath = NULL;
> - int fd = -1;
> - struct sockaddr_un addr;
> -
> - if (virAsprintf(&sockpath, "%s/%s.sock",
> - driver->stateDir, vm->def->name) < 0) {
> - virReportOOMError();
> - return -1;
> - }
> -
> - if (virSecurityManagerSetSocketLabel(driver->securityManager, vm->def) < 0) {
> - VIR_ERROR(_("Failed to set security context for monitor for %s"),
> - vm->def->name);
> - goto error;
> - }
> -
> - fd = socket(PF_UNIX, SOCK_STREAM, 0);
> -
> - if (virSecurityManagerClearSocketLabel(driver->securityManager, vm->def) < 0) {
> - VIR_ERROR(_("Failed to clear security context for monitor for %s"),
> - vm->def->name);
> - goto error;
> - }
> -
> - if (fd < 0) {
> - virReportSystemError(errno, "%s",
> - _("Failed to create client socket"));
> - goto error;
> - }
> -
> - memset(&addr, 0, sizeof(addr));
> - addr.sun_family = AF_UNIX;
> - if (virStrcpyStatic(addr.sun_path, sockpath) == NULL) {
> - lxcError(VIR_ERR_INTERNAL_ERROR,
> - _("Socket path %s too big for destination"), sockpath);
> - goto error;
> - }
> -
> - if (connect(fd, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
> - virReportSystemError(errno, "%s",
> - _("Failed to connect to client socket"));
> - goto error;
> - }
> -
> - VIR_FREE(sockpath);
> - return fd;
> -
> -error:
> - VIR_FREE(sockpath);
> - VIR_FORCE_CLOSE(fd);
> - return -1;
> -}
> -
> -
> -static int lxcVmTerminate(lxc_driver_t *driver,
> - virDomainObjPtr vm,
> - virDomainShutoffReason reason)
> -{
> - virCgroupPtr group = NULL;
> - int rc;
> -
> - if (vm->pid <= 0) {
> - lxcError(VIR_ERR_INTERNAL_ERROR,
> - _("Invalid PID %d for container"), vm->pid);
> - return -1;
> - }
> -
> - virSecurityManagerRestoreAllLabel(driver->securityManager,
> - vm->def, false);
> - virSecurityManagerReleaseLabel(driver->securityManager, vm->def);
> - /* Clear out dynamically assigned labels */
> - if (vm->def->seclabel.type == VIR_DOMAIN_SECLABEL_DYNAMIC) {
> - VIR_FREE(vm->def->seclabel.model);
> - VIR_FREE(vm->def->seclabel.label);
> - VIR_FREE(vm->def->seclabel.imagelabel);
> - }
> -
> - if (virCgroupForDomain(driver->cgroup, vm->def->name, &group, 0) == 0) {
> - rc = virCgroupKillPainfully(group);
> - if (rc < 0) {
> - virReportSystemError(-rc, "%s",
> - _("Failed to kill container PIDs"));
> - rc = -1;
> - goto cleanup;
> - }
> - if (rc == 1) {
> - lxcError(VIR_ERR_INTERNAL_ERROR, "%s",
> - _("Some container PIDs refused to die"));
> - rc = -1;
> - goto cleanup;
> - }
> - } else {
> - /* If cgroup doesn't exist, the VM pids must have already
> - * died and so we're just cleaning up stale state
> - */
> - }
> -
> - lxcVmCleanup(driver, vm, reason);
> -
> - rc = 0;
> -
> -cleanup:
> - virCgroupFree(&group);
> - return rc;
> -}
> -
> -static void lxcMonitorEvent(int watch,
> - int fd,
> - int events ATTRIBUTE_UNUSED,
> - void *data)
> -{
> - lxc_driver_t *driver = lxc_driver;
> - virDomainObjPtr vm = data;
> - virDomainEventPtr event = NULL;
> - lxcDomainObjPrivatePtr priv;
> -
> - lxcDriverLock(driver);
> - virDomainObjLock(vm);
> - lxcDriverUnlock(driver);
> -
> - priv = vm->privateData;
> -
> - if (priv->monitor != fd || priv->monitorWatch != watch) {
> - virEventRemoveHandle(watch);
> - goto cleanup;
> - }
> -
> - if (lxcVmTerminate(driver, vm, VIR_DOMAIN_SHUTOFF_SHUTDOWN) < 0) {
> - virEventRemoveHandle(watch);
> - } else {
> - event = virDomainEventNewFromObj(vm,
> - VIR_DOMAIN_EVENT_STOPPED,
> - VIR_DOMAIN_EVENT_STOPPED_SHUTDOWN);
> - virDomainAuditStop(vm, "shutdown");
> - }
> - if (!vm->persistent) {
> - virDomainRemoveInactive(&driver->domains, vm);
> - vm = NULL;
> - }
> -
> -cleanup:
> - if (vm)
> - virDomainObjUnlock(vm);
> - if (event) {
> - lxcDriverLock(driver);
> - lxcDomainEventQueue(driver, event);
> - lxcDriverUnlock(driver);
> - }
> -}
> -
> -
> -static virCommandPtr
> -lxcBuildControllerCmd(lxc_driver_t *driver,
> - virDomainObjPtr vm,
> - int nveths,
> - char **veths,
> - int *ttyFDs,
> - size_t nttyFDs,
> - int handshakefd)
> -{
> - size_t i;
> - char *filterstr;
> - char *outputstr;
> - virCommandPtr cmd;
> -
> - cmd = virCommandNew(vm->def->emulator);
> -
> - /* The controller may call ip command, so we have to retain PATH. */
> - virCommandAddEnvPass(cmd, "PATH");
> -
> - virCommandAddEnvFormat(cmd, "LIBVIRT_DEBUG=%d",
> - virLogGetDefaultPriority());
> -
> - if (virLogGetNbFilters() > 0) {
> - filterstr = virLogGetFilters();
> - if (!filterstr) {
> - virReportOOMError();
> - goto cleanup;
> - }
> -
> - virCommandAddEnvPair(cmd, "LIBVIRT_LOG_FILTERS", filterstr);
> - VIR_FREE(filterstr);
> - }
> -
> - if (driver->log_libvirtd) {
> - if (virLogGetNbOutputs() > 0) {
> - outputstr = virLogGetOutputs();
> - if (!outputstr) {
> - virReportOOMError();
> - goto cleanup;
> - }
> -
> - virCommandAddEnvPair(cmd, "LIBVIRT_LOG_OUTPUTS", outputstr);
> - VIR_FREE(outputstr);
> - }
> - } else {
> - virCommandAddEnvFormat(cmd,
> - "LIBVIRT_LOG_OUTPUTS=%d:stderr",
> - virLogGetDefaultPriority());
> - }
> -
> - virCommandAddArgList(cmd, "--name", vm->def->name, NULL);
> - for (i = 0 ; i < nttyFDs ; i++) {
> - virCommandAddArg(cmd, "--console");
> - virCommandAddArgFormat(cmd, "%d", ttyFDs[i]);
> - virCommandPreserveFD(cmd, ttyFDs[i]);
> - }
> -
> - virCommandAddArgPair(cmd, "--security",
> - virSecurityManagerGetModel(driver->securityManager));
> -
> - virCommandAddArg(cmd, "--handshake");
> - virCommandAddArgFormat(cmd, "%d", handshakefd);
> - virCommandAddArg(cmd, "--background");
> -
> - for (i = 0 ; i < nveths ; i++) {
> - virCommandAddArgList(cmd, "--veth", veths[i], NULL);
> - }
> -
> - virCommandPreserveFD(cmd, handshakefd);
> -
> - return cmd;
> -cleanup:
> - virCommandFree(cmd);
> - return NULL;
> -}
> -
> -static int
> -lxcReadLogOutput(virDomainObjPtr vm,
> - char *logfile,
> - off_t pos,
> - char *buf,
> - size_t buflen)
> -{
> - int fd;
> - off_t off;
> - int whence;
> - int got = 0, ret = -1;
> - int retries = 10;
> -
> - if ((fd = open(logfile, O_RDONLY)) < 0) {
> - virReportSystemError(errno, _("failed to open logfile %s"),
> - logfile);
> - goto cleanup;
> - }
> -
> - if (pos < 0) {
> - off = 0;
> - whence = SEEK_END;
> - } else {
> - off = pos;
> - whence = SEEK_SET;
> - }
> -
> - if (lseek(fd, off, whence) < 0) {
> - if (whence == SEEK_END)
> - virReportSystemError(errno,
> - _("unable to seek to end of log for %s"),
> - logfile);
> - else
> - virReportSystemError(errno,
> - _("unable to seek to %lld from start for %s"),
> - (long long)off, logfile);
> - goto cleanup;
> - }
> -
> - while (retries) {
> - ssize_t bytes;
> - int isdead = 0;
> -
> - if (kill(vm->pid, 0) == -1 && errno == ESRCH)
> - isdead = 1;
> -
> - /* Any failures should be detected before we read the log, so we
> - * always have something useful to report on failure. */
> - bytes = saferead(fd, buf+got, buflen-got-1);
> - if (bytes < 0) {
> - virReportSystemError(errno, "%s",
> - _("Failure while reading guest log output"));
> - goto cleanup;
> - }
> -
> - got += bytes;
> - buf[got] = '\0';
> -
> - if ((got == buflen-1) || isdead) {
> - break;
> - }
> -
> - usleep(100*1000);
> - retries--;
> - }
> -
> -
> - ret = got;
> -cleanup:
> - VIR_FORCE_CLOSE(fd);
> - return ret;
> -}
> -
> -/**
> - * lxcVmStart:
> - * @conn: pointer to connection
> - * @driver: pointer to driver structure
> - * @vm: pointer to virtual machine structure
> - * @autoDestroy: mark the domain for auto destruction
> - * @reason: reason for switching vm to running state
> - *
> - * Starts a vm
> - *
> - * Returns 0 on success or -1 in case of error
> - */
> -static int lxcVmStart(virConnectPtr conn,
> - lxc_driver_t * driver,
> - virDomainObjPtr vm,
> - bool autoDestroy,
> - virDomainRunningReason reason)
> -{
> - int rc = -1, r;
> - size_t nttyFDs = 0;
> - int *ttyFDs = NULL;
> - size_t i;
> - char *logfile = NULL;
> - int logfd = -1;
> - unsigned int nveths = 0;
> - char **veths = NULL;
> - int handshakefds[2] = { -1, -1 };
> - off_t pos = -1;
> - char ebuf[1024];
> - char *timestamp;
> - virCommandPtr cmd = NULL;
> - lxcDomainObjPrivatePtr priv = vm->privateData;
> - virErrorPtr err = NULL;
> -
> - if (!lxc_driver->cgroup) {
> - lxcError(VIR_ERR_INTERNAL_ERROR, "%s",
> - _("The 'cpuacct', 'devices' & 'memory' cgroups controllers must be mounted"));
> - return -1;
> - }
> -
> - if (!virCgroupMounted(lxc_driver->cgroup,
> - VIR_CGROUP_CONTROLLER_CPUACCT)) {
> - lxcError(VIR_ERR_INTERNAL_ERROR, "%s",
> - _("Unable to find 'cpuacct' cgroups controller mount"));
> - return -1;
> - }
> - if (!virCgroupMounted(lxc_driver->cgroup,
> - VIR_CGROUP_CONTROLLER_DEVICES)) {
> - lxcError(VIR_ERR_INTERNAL_ERROR, "%s",
> - _("Unable to find 'devices' cgroups controller mount"));
> - return -1;
> - }
> - if (!virCgroupMounted(lxc_driver->cgroup,
> - VIR_CGROUP_CONTROLLER_MEMORY)) {
> - lxcError(VIR_ERR_INTERNAL_ERROR, "%s",
> - _("Unable to find 'memory' cgroups controller mount"));
> - return -1;
> - }
> -
> - if (virFileMakePath(driver->logDir) < 0) {
> - virReportSystemError(errno,
> - _("Cannot create log directory '%s'"),
> - driver->logDir);
> - return -1;
> - }
> -
> - if (virAsprintf(&logfile, "%s/%s.log",
> - driver->logDir, vm->def->name) < 0) {
> - virReportOOMError();
> - return -1;
> - }
> -
> - /* Do this up front, so any part of the startup process can add
> - * runtime state to vm->def that won't be persisted. This let's us
> - * report implicit runtime defaults in the XML, like vnc listen/socket
> - */
> - VIR_DEBUG("Setting current domain def as transient");
> - if (virDomainObjSetDefTransient(driver->caps, vm, true) < 0)
> - goto cleanup;
> -
> - /* Run an early hook to set-up missing devices */
> - if (virHookPresent(VIR_HOOK_DRIVER_LXC)) {
> - char *xml = virDomainDefFormat(vm->def, 0);
> - int hookret;
> -
> - hookret = virHookCall(VIR_HOOK_DRIVER_LXC, vm->def->name,
> - VIR_HOOK_LXC_OP_PREPARE, VIR_HOOK_SUBOP_BEGIN,
> - NULL, xml, NULL);
> - VIR_FREE(xml);
> -
> - /*
> - * If the script raised an error abort the launch
> - */
> - if (hookret < 0)
> - goto cleanup;
> - }
> -
> - /* Here we open all the PTYs we need on the host OS side.
> - * The LXC controller will open the guest OS side PTYs
> - * and forward I/O between them.
> - */
> - nttyFDs = vm->def->nconsoles;
> - if (VIR_ALLOC_N(ttyFDs, nttyFDs) < 0) {
> - virReportOOMError();
> - goto cleanup;
> - }
> -
> - /* If you are using a SecurityDriver with dynamic labelling,
> - then generate a security label for isolation */
> - VIR_DEBUG("Generating domain security label (if required)");
> - if (vm->def->seclabel.type == VIR_DOMAIN_SECLABEL_DEFAULT)
> - vm->def->seclabel.type = VIR_DOMAIN_SECLABEL_NONE;
> -
> - if (virSecurityManagerGenLabel(driver->securityManager, vm->def) < 0) {
> - virDomainAuditSecurityLabel(vm, false);
> - goto cleanup;
> - }
> - virDomainAuditSecurityLabel(vm, true);
> -
> - VIR_DEBUG("Setting domain security labels");
> - if (virSecurityManagerSetAllLabel(driver->securityManager,
> - vm->def, NULL) < 0)
> - goto cleanup;
> -
> - for (i = 0 ; i < vm->def->nconsoles ; i++)
> - ttyFDs[i] = -1;
> -
> - for (i = 0 ; i < vm->def->nconsoles ; i++) {
> - char *ttyPath;
> - if (vm->def->consoles[i]->source.type != VIR_DOMAIN_CHR_TYPE_PTY) {
> - lxcError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
> - _("Only PTY console types are supported"));
> - goto cleanup;
> - }
> -
> - if (virFileOpenTty(&ttyFDs[i], &ttyPath, 1) < 0) {
> - virReportSystemError(errno, "%s",
> - _("Failed to allocate tty"));
> - goto cleanup;
> - }
> -
> - VIR_FREE(vm->def->consoles[i]->source.data.file.path);
> - vm->def->consoles[i]->source.data.file.path = ttyPath;
> -
> - VIR_FREE(vm->def->consoles[i]->info.alias);
> - if (virAsprintf(&vm->def->consoles[i]->info.alias, "console%zu", i) < 0) {
> - virReportOOMError();
> - goto cleanup;
> - }
> - }
> -
> - if (lxcSetupInterfaces(conn, vm->def, &nveths, &veths) != 0)
> - goto cleanup;
> -
> - /* Save the configuration for the controller */
> - if (virDomainSaveConfig(driver->stateDir, vm->def) < 0)
> - goto cleanup;
> -
> - if ((logfd = open(logfile, O_WRONLY | O_APPEND | O_CREAT,
> - S_IRUSR|S_IWUSR)) < 0) {
> - virReportSystemError(errno,
> - _("Failed to open '%s'"),
> - logfile);
> - goto cleanup;
> - }
> -
> - if (pipe(handshakefds) < 0) {
> - virReportSystemError(errno, "%s",
> - _("Unable to create pipe"));
> - goto cleanup;
> - }
> -
> - if (!(cmd = lxcBuildControllerCmd(driver,
> - vm,
> - nveths, veths,
> - ttyFDs, nttyFDs,
> - handshakefds[1])))
> - goto cleanup;
> - virCommandSetOutputFD(cmd, &logfd);
> - virCommandSetErrorFD(cmd, &logfd);
> -
> - /* now that we know it is about to start call the hook if present */
> - if (virHookPresent(VIR_HOOK_DRIVER_LXC)) {
> - char *xml = virDomainDefFormat(vm->def, 0);
> - int hookret;
> -
> - hookret = virHookCall(VIR_HOOK_DRIVER_LXC, vm->def->name,
> - VIR_HOOK_LXC_OP_START, VIR_HOOK_SUBOP_BEGIN,
> - NULL, xml, NULL);
> - VIR_FREE(xml);
> -
> - /*
> - * If the script raised an error abort the launch
> - */
> - if (hookret < 0)
> - goto cleanup;
> - }
> -
> - /* Log timestamp */
> - if ((timestamp = virTimeStringNow()) == NULL) {
> - virReportOOMError();
> - goto cleanup;
> - }
> - if (safewrite(logfd, timestamp, strlen(timestamp)) < 0 ||
> - safewrite(logfd, START_POSTFIX, strlen(START_POSTFIX)) < 0) {
> - VIR_WARN("Unable to write timestamp to logfile: %s",
> - virStrerror(errno, ebuf, sizeof(ebuf)));
> - }
> - VIR_FREE(timestamp);
> -
> - /* Log generated command line */
> - virCommandWriteArgLog(cmd, logfd);
> - if ((pos = lseek(logfd, 0, SEEK_END)) < 0)
> - VIR_WARN("Unable to seek to end of logfile: %s",
> - virStrerror(errno, ebuf, sizeof(ebuf)));
> -
> - if (virCommandRun(cmd, NULL) < 0)
> - goto cleanup;
> -
> - if (VIR_CLOSE(handshakefds[1]) < 0) {
> - virReportSystemError(errno, "%s", _("could not close handshake fd"));
> - goto cleanup;
> - }
> -
> - /* Connect to the controller as a client *first* because
> - * this will block until the child has written their
> - * pid file out to disk */
> - if ((priv->monitor = lxcMonitorClient(driver, vm)) < 0)
> - goto cleanup;
> -
> - /* And get its pid */
> - if ((r = virPidFileRead(driver->stateDir, vm->def->name, &vm->pid)) < 0) {
> - virReportSystemError(-r,
> - _("Failed to read pid file %s/%s.pid"),
> - driver->stateDir, vm->def->name);
> - goto cleanup;
> - }
> -
> - vm->def->id = vm->pid;
> - virDomainObjSetState(vm, VIR_DOMAIN_RUNNING, reason);
> -
> - if (lxcContainerWaitForContinue(handshakefds[0]) < 0) {
> - char out[1024];
> -
> - if (!(lxcReadLogOutput(vm, logfile, pos, out, 1024) < 0)) {
> - lxcError(VIR_ERR_INTERNAL_ERROR,
> - _("guest failed to start: %s"), out);
> - }
> -
> - goto error;
> - }
> -
> - if ((priv->monitorWatch = virEventAddHandle(
> - priv->monitor,
> - VIR_EVENT_HANDLE_ERROR | VIR_EVENT_HANDLE_HANGUP,
> - lxcMonitorEvent,
> - vm, NULL)) < 0) {
> - goto error;
> - }
> -
> - if (autoDestroy &&
> - lxcProcessAutoDestroyAdd(driver, vm, conn) < 0)
> - goto error;
> -
> - if (virDomainObjSetDefTransient(driver->caps, vm, false) < 0)
> - goto error;
> -
> - /* Write domain status to disk.
> - *
> - * XXX: Earlier we wrote the plain "live" domain XML to this
> - * location for the benefit of libvirt_lxc. We're now overwriting
> - * it with the live status XML instead. This is a (currently
> - * harmless) inconsistency we should fix one day */
> - if (virDomainSaveStatus(driver->caps, driver->stateDir, vm) < 0)
> - goto error;
> -
> - /* finally we can call the 'started' hook script if any */
> - if (virHookPresent(VIR_HOOK_DRIVER_LXC)) {
> - char *xml = virDomainDefFormat(vm->def, 0);
> - int hookret;
> -
> - hookret = virHookCall(VIR_HOOK_DRIVER_LXC, vm->def->name,
> - VIR_HOOK_LXC_OP_STARTED, VIR_HOOK_SUBOP_BEGIN,
> - NULL, xml, NULL);
> - VIR_FREE(xml);
> -
> - /*
> - * If the script raised an error abort the launch
> - */
> - if (hookret < 0)
> - goto error;
> - }
> -
> - rc = 0;
> -
> -cleanup:
> - if (rc != 0 && !err)
> - err = virSaveLastError();
> - virCommandFree(cmd);
> - if (VIR_CLOSE(logfd) < 0) {
> - virReportSystemError(errno, "%s", _("could not close logfile"));
> - rc = -1;
> - }
> - for (i = 0 ; i < nveths ; i++) {
> - if (rc != 0)
> - ignore_value(virNetDevVethDelete(veths[i]));
> - VIR_FREE(veths[i]);
> - }
> - if (rc != 0) {
> - VIR_FORCE_CLOSE(priv->monitor);
> - virDomainConfVMNWFilterTeardown(vm);
> -
> - virSecurityManagerRestoreAllLabel(driver->securityManager,
> - vm->def, false);
> - virSecurityManagerReleaseLabel(driver->securityManager, vm->def);
> - /* Clear out dynamically assigned labels */
> - if (vm->def->seclabel.type == VIR_DOMAIN_SECLABEL_DYNAMIC) {
> - VIR_FREE(vm->def->seclabel.model);
> - VIR_FREE(vm->def->seclabel.label);
> - VIR_FREE(vm->def->seclabel.imagelabel);
> - }
> - }
> - for (i = 0 ; i < nttyFDs ; i++)
> - VIR_FORCE_CLOSE(ttyFDs[i]);
> - VIR_FREE(ttyFDs);
> - VIR_FORCE_CLOSE(handshakefds[0]);
> - VIR_FORCE_CLOSE(handshakefds[1]);
> - VIR_FREE(logfile);
> -
> - if (err) {
> - virSetError(err);
> - virFreeError(err);
> - }
> -
> - return rc;
> -
> -error:
> - err = virSaveLastError();
> - lxcVmTerminate(driver, vm, VIR_DOMAIN_SHUTOFF_FAILED);
> - goto cleanup;
> -}
> -
> /**
> * lxcDomainStartWithFlags:
> * @dom: domain to start
> @@ -2089,7 +995,7 @@ cleanup:
> if (vm)
> virDomainObjUnlock(vm);
> if (event)
> - lxcDomainEventQueue(driver, event);
> + virDomainEventStateQueue(driver->domainEventState, event);
> lxcDriverUnlock(driver);
> return ret;
> }
> @@ -2176,7 +1082,7 @@ cleanup:
> if (vm)
> virDomainObjUnlock(vm);
> if (event)
> - lxcDomainEventQueue(driver, event);
> + virDomainEventStateQueue(driver->domainEventState, event);
> lxcDriverUnlock(driver);
> return dom;
> }
> @@ -2354,13 +1260,6 @@ lxcDomainEventDeregisterAny(virConnectPtr conn,
> }
>
>
> -/* driver must be locked before calling */
> -static void lxcDomainEventQueue(lxc_driver_t *driver,
> - virDomainEventPtr event)
> -{
> - virDomainEventStateQueue(driver->domainEventState, event);
> -}
> -
> /**
> * lxcDomainDestroyFlags:
> * @dom: pointer to domain to destroy
> @@ -2411,7 +1310,7 @@ cleanup:
> if (vm)
> virDomainObjUnlock(vm);
> if (event)
> - lxcDomainEventQueue(driver, event);
> + virDomainEventStateQueue(driver->domainEventState, event);
> lxcDriverUnlock(driver);
> return ret;
> }
> @@ -2446,121 +1345,6 @@ static int lxcCheckNetNsSupport(void)
> }
>
>
> -struct lxcAutostartData {
> - lxc_driver_t *driver;
> - virConnectPtr conn;
> -};
> -
> -static void
> -lxcAutostartDomain(void *payload, const void *name ATTRIBUTE_UNUSED, void *opaque)
> -{
> - virDomainObjPtr vm = payload;
> - const struct lxcAutostartData *data = opaque;
> -
> - virDomainObjLock(vm);
> - if (vm->autostart &&
> - !virDomainObjIsActive(vm)) {
> - int ret = lxcVmStart(data->conn, data->driver, vm, false,
> - VIR_DOMAIN_RUNNING_BOOTED);
> - virDomainAuditStart(vm, "booted", ret >= 0);
> - if (ret < 0) {
> - virErrorPtr err = virGetLastError();
> - VIR_ERROR(_("Failed to autostart VM '%s': %s"),
> - vm->def->name,
> - err ? err->message : "");
> - } else {
> - virDomainEventPtr event =
> - virDomainEventNewFromObj(vm,
> - VIR_DOMAIN_EVENT_STARTED,
> - VIR_DOMAIN_EVENT_STARTED_BOOTED);
> - if (event)
> - lxcDomainEventQueue(data->driver, event);
> - }
> - }
> - virDomainObjUnlock(vm);
> -}
> -
> -static void
> -lxcAutostartConfigs(lxc_driver_t *driver) {
> - /* XXX: Figure out a better way todo this. The domain
> - * startup code needs a connection handle in order
> - * to lookup the bridge associated with a virtual
> - * network
> - */
> - virConnectPtr conn = virConnectOpen("lxc:///");
> - /* Ignoring NULL conn which is mostly harmless here */
> -
> - struct lxcAutostartData data = { driver, conn };
> -
> - lxcDriverLock(driver);
> - virHashForEach(driver->domains.objs, lxcAutostartDomain, &data);
> - lxcDriverUnlock(driver);
> -
> - if (conn)
> - virConnectClose(conn);
> -}
> -
> -static void
> -lxcReconnectVM(void *payload, const void *name ATTRIBUTE_UNUSED, void *opaque)
> -{
> - virDomainObjPtr vm = payload;
> - lxc_driver_t *driver = opaque;
> - lxcDomainObjPrivatePtr priv;
> -
> - virDomainObjLock(vm);
> - VIR_DEBUG("Reconnect %d %d %d\n", vm->def->id, vm->pid, vm->state.state);
> -
> - priv = vm->privateData;
> -
> - if (vm->pid != 0) {
> - vm->def->id = vm->pid;
> - virDomainObjSetState(vm, VIR_DOMAIN_RUNNING,
> - VIR_DOMAIN_RUNNING_UNKNOWN);
> -
> - if ((priv->monitor = lxcMonitorClient(driver, vm)) < 0)
> - goto error;
> -
> - if ((priv->monitorWatch = virEventAddHandle(
> - priv->monitor,
> - VIR_EVENT_HANDLE_ERROR | VIR_EVENT_HANDLE_HANGUP,
> - lxcMonitorEvent,
> - vm, NULL)) < 0)
> - goto error;
> -
> - if (virSecurityManagerReserveLabel(driver->securityManager,
> - vm->def, vm->pid) < 0)
> - goto error;
> -
> - /* now that we know it's reconnected call the hook if present */
> - if (virHookPresent(VIR_HOOK_DRIVER_LXC)) {
> - char *xml = virDomainDefFormat(vm->def, 0);
> - int hookret;
> -
> - /* we can't stop the operation even if the script raised an error */
> - hookret = virHookCall(VIR_HOOK_DRIVER_LXC, vm->def->name,
> - VIR_HOOK_LXC_OP_RECONNECT, VIR_HOOK_SUBOP_BEGIN,
> - NULL, xml, NULL);
> - VIR_FREE(xml);
> - if (hookret < 0)
> - goto error;
> - }
> -
> - } else {
> - vm->def->id = -1;
> - VIR_FORCE_CLOSE(priv->monitor);
> - }
> -
> -cleanup:
> - virDomainObjUnlock(vm);
> - return;
> -
> -error:
> - lxcVmTerminate(driver, vm, VIR_DOMAIN_SHUTOFF_FAILED);
> - virDomainAuditStop(vm, "failed");
> - goto cleanup;
> -}
> -
> -
> static int
> lxcSecurityInit(lxc_driver_t *driver)
> {
> @@ -2664,7 +1448,7 @@ static int lxcStartup(int privileged)
> NULL, NULL) < 0)
> goto cleanup;
>
> - virHashForEach(lxc_driver->domains.objs, lxcReconnectVM, lxc_driver);
> + lxcReconnectAll(lxc_driver, &lxc_driver->domains);
>
> /* Then inactive persistent configs */
> if (virDomainLoadAllConfigs(lxc_driver->caps,
> @@ -2697,7 +1481,7 @@ static void lxcNotifyLoadDomain(virDomainObjPtr vm, int newVM, void *opaque)
> VIR_DOMAIN_EVENT_DEFINED,
> VIR_DOMAIN_EVENT_DEFINED_ADDED);
> if (event)
> - lxcDomainEventQueue(driver, event);
> + virDomainEventStateQueue(driver->domainEventState, event);
> }
> }
>
> @@ -3706,7 +2490,7 @@ static int lxcDomainSuspend(virDomainPtr dom)
>
> cleanup:
> if (event)
> - lxcDomainEventQueue(driver, event);
> + virDomainEventStateQueue(driver->domainEventState, event);
> if (vm)
> virDomainObjUnlock(vm);
> lxcDriverUnlock(driver);
> @@ -3772,7 +2556,7 @@ static int lxcDomainResume(virDomainPtr dom)
>
> cleanup:
> if (event)
> - lxcDomainEventQueue(driver, event);
> + virDomainEventStateQueue(driver->domainEventState, event);
> if (vm)
> virDomainObjUnlock(vm);
> lxcDriverUnlock(driver);
> diff --git a/src/lxc/lxc_process.c b/src/lxc/lxc_process.c
> new file mode 100644
> index 0000000..12f6ae6
> --- /dev/null
> +++ b/src/lxc/lxc_process.c
> @@ -0,0 +1,1242 @@
> +/*
> + * Copyright (C) 2010-2012 Red Hat, Inc.
> + * Copyright IBM Corp. 2008
> + *
> + * lxc_process.c: LXC process lifecycle management
> + *
> + * 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
> + */
> +
> +#include <config.h>
> +
> +#include <unistd.h>
> +#include <fcntl.h>
> +#include <signal.h>
> +
> +#include "lxc_process.h"
> +#include "lxc_domain.h"
> +#include "lxc_container.h"
> +#include "datatypes.h"
> +#include "virfile.h"
> +#include "virpidfile.h"
> +#include "virnetdev.h"
> +#include "virnetdevveth.h"
> +#include "virnetdevbridge.h"
> +#include "virtime.h"
> +#include "domain_nwfilter.h"
> +#include "network/bridge_driver.h"
> +#include "memory.h"
> +#include "domain_audit.h"
> +#include "virterror_internal.h"
> +#include "logging.h"
> +#include "command.h"
> +#include "hooks.h"
> +
> +#define VIR_FROM_THIS VIR_FROM_LXC
> +
> +#define START_POSTFIX ": starting up\n"
> +
> +int lxcProcessAutoDestroyInit(lxc_driver_t *driver)
> +{
> + if (!(driver->autodestroy = virHashCreate(5, NULL)))
> + return -1;
> +
> + return 0;
> +}
> +
> +struct lxcProcessAutoDestroyData {
> + lxc_driver_t *driver;
> + virConnectPtr conn;
> +};
> +
> +static void lxcProcessAutoDestroyDom(void *payload,
> + const void *name,
> + void *opaque)
> +{
> + struct lxcProcessAutoDestroyData *data = opaque;
> + virConnectPtr conn = payload;
> + const char *uuidstr = name;
> + unsigned char uuid[VIR_UUID_BUFLEN];
> + virDomainObjPtr dom;
> + virDomainEventPtr event = NULL;
> +
> + VIR_DEBUG("conn=%p uuidstr=%s thisconn=%p", conn, uuidstr, data->conn);
> +
> + if (data->conn != conn)
> + return;
> +
> + if (virUUIDParse(uuidstr, uuid) < 0) {
> + VIR_WARN("Failed to parse %s", uuidstr);
> + return;
> + }
> +
> + if (!(dom = virDomainFindByUUID(&data->driver->domains,
> + uuid))) {
> + VIR_DEBUG("No domain object to kill");
> + return;
> + }
> +
> + VIR_DEBUG("Killing domain");
> + lxcVmTerminate(data->driver, dom, VIR_DOMAIN_SHUTOFF_DESTROYED);
> + virDomainAuditStop(dom, "destroyed");
> + event = virDomainEventNewFromObj(dom,
> + VIR_DOMAIN_EVENT_STOPPED,
> + VIR_DOMAIN_EVENT_STOPPED_DESTROYED);
> +
> + if (dom && !dom->persistent)
> + virDomainRemoveInactive(&data->driver->domains, dom);
> +
> + if (dom)
> + virDomainObjUnlock(dom);
> + if (event)
> + virDomainEventStateQueue(data->driver->domainEventState, event);
> + virHashRemoveEntry(data->driver->autodestroy, uuidstr);
> +}
> +
> +/*
> + * Precondition: driver is locked
> + */
> +void lxcProcessAutoDestroyRun(lxc_driver_t *driver, virConnectPtr conn)
> +{
> + struct lxcProcessAutoDestroyData data = {
> + driver, conn
> + };
> + VIR_DEBUG("conn=%p", conn);
> + virHashForEach(driver->autodestroy, lxcProcessAutoDestroyDom, &data);
> +}
> +
> +void lxcProcessAutoDestroyShutdown(lxc_driver_t *driver)
> +{
> + virHashFree(driver->autodestroy);
> +}
> +
> +int lxcProcessAutoDestroyAdd(lxc_driver_t *driver,
> + virDomainObjPtr vm,
> + virConnectPtr conn)
> +{
> + char uuidstr[VIR_UUID_STRING_BUFLEN];
> + virUUIDFormat(vm->def->uuid, uuidstr);
> + VIR_DEBUG("vm=%s uuid=%s conn=%p", vm->def->name, uuidstr, conn);
> + if (virHashAddEntry(driver->autodestroy, uuidstr, conn) < 0)
> + return -1;
> + return 0;
> +}
> +
> +int lxcProcessAutoDestroyRemove(lxc_driver_t *driver,
> + virDomainObjPtr vm)
> +{
> + char uuidstr[VIR_UUID_STRING_BUFLEN];
> + virUUIDFormat(vm->def->uuid, uuidstr);
> + VIR_DEBUG("vm=%s uuid=%s", vm->def->name, uuidstr);
> + if (virHashRemoveEntry(driver->autodestroy, uuidstr) < 0)
> + return -1;
> + return 0;
> +}
> +
> +
> +/**
> + * lxcVmCleanup:
> + * @driver: pointer to driver structure
> + * @vm: pointer to VM to clean up
> + * @reason: reason for switching the VM to shutoff state
> + *
> + * Cleanout resources associated with the now dead VM
> + *
> + */
> +static void lxcVmCleanup(lxc_driver_t *driver,
> + virDomainObjPtr vm,
> + virDomainShutoffReason reason)
> +{
> + virCgroupPtr cgroup;
> + int i;
> + lxcDomainObjPrivatePtr priv = vm->privateData;
> + virNetDevVPortProfilePtr vport = NULL;
> +
> + /* now that we know it's stopped call the hook if present */
> + if (virHookPresent(VIR_HOOK_DRIVER_LXC)) {
> + char *xml = virDomainDefFormat(vm->def, 0);
> +
> + /* we can't stop the operation even if the script raised an error */
> + virHookCall(VIR_HOOK_DRIVER_LXC, vm->def->name,
> + VIR_HOOK_LXC_OP_STOPPED, VIR_HOOK_SUBOP_END,
> + NULL, xml, NULL);
> + VIR_FREE(xml);
> + }
> +
> + /* Stop autodestroy in case guest is restarted */
> + lxcProcessAutoDestroyRemove(driver, vm);
> +
> + virEventRemoveHandle(priv->monitorWatch);
> + VIR_FORCE_CLOSE(priv->monitor);
> +
> + virPidFileDelete(driver->stateDir, vm->def->name);
> + virDomainDeleteConfig(driver->stateDir, NULL, vm);
> +
> + virDomainObjSetState(vm, VIR_DOMAIN_SHUTOFF, reason);
> + vm->pid = -1;
> + vm->def->id = -1;
> + priv->monitor = -1;
> + priv->monitorWatch = -1;
> +
> + for (i = 0 ; i < vm->def->nnets ; i++) {
> + virDomainNetDefPtr iface = vm->def->nets[i];
> + vport = virDomainNetGetActualVirtPortProfile(iface);
> + ignore_value(virNetDevSetOnline(iface->ifname, false));
> + if (vport && vport->virtPortType == VIR_NETDEV_VPORT_PROFILE_OPENVSWITCH)
> + ignore_value(virNetDevOpenvswitchRemovePort(
> + virDomainNetGetActualBridgeName(iface),
> + iface->ifname));
> + ignore_value(virNetDevVethDelete(iface->ifname));
> + networkReleaseActualDevice(iface);
> + }
> +
> + virDomainConfVMNWFilterTeardown(vm);
> +
> + if (driver->cgroup &&
> + virCgroupForDomain(driver->cgroup, vm->def->name, &cgroup, 0) == 0) {
> + virCgroupRemove(cgroup);
> + virCgroupFree(&cgroup);
> + }
> +
> + /* now that we know it's stopped call the hook if present */
> + if (virHookPresent(VIR_HOOK_DRIVER_LXC)) {
> + char *xml = virDomainDefFormat(vm->def, 0);
> +
> + /* we can't stop the operation even if the script raised an error */
> + virHookCall(VIR_HOOK_DRIVER_LXC, vm->def->name,
> + VIR_HOOK_LXC_OP_RELEASE, VIR_HOOK_SUBOP_END,
> + NULL, xml, NULL);
> + VIR_FREE(xml);
> + }
> +
> + if (vm->newDef) {
> + virDomainDefFree(vm->def);
> + vm->def = vm->newDef;
> + vm->def->id = -1;
> + vm->newDef = NULL;
> + }
> +}
> +
> +
> +static int lxcSetupInterfaceBridged(virConnectPtr conn,
> + virDomainDefPtr vm,
> + virDomainNetDefPtr net,
> + const char *brname,
> + unsigned int *nveths,
> + char ***veths)
> +{
> + int ret = -1;
> + char *parentVeth;
> + char *containerVeth = NULL;
> + const virNetDevVPortProfilePtr vport = virDomainNetGetActualVirtPortProfile(net);
> +
> + VIR_DEBUG("calling vethCreate()");
> + parentVeth = net->ifname;
> + if (virNetDevVethCreate(&parentVeth, &containerVeth) < 0)
> + goto cleanup;
> + VIR_DEBUG("parentVeth: %s, containerVeth: %s", parentVeth, containerVeth);
> +
> + if (net->ifname == NULL)
> + net->ifname = parentVeth;
> +
> + if (VIR_REALLOC_N(*veths, (*nveths)+1) < 0) {
> + virReportOOMError();
> + VIR_FREE(containerVeth);
> + goto cleanup;
> + }
> + (*veths)[(*nveths)] = containerVeth;
> + (*nveths)++;
> +
> + if (virNetDevSetMAC(containerVeth, &net->mac) < 0)
> + goto cleanup;
> +
> + if (vport && vport->virtPortType == VIR_NETDEV_VPORT_PROFILE_OPENVSWITCH)
> + ret = virNetDevOpenvswitchAddPort(brname, parentVeth, &net->mac,
> + vm->uuid, vport);
> + else
> + ret = virNetDevBridgeAddPort(brname, parentVeth);
> + if (ret < 0)
> + goto cleanup;
> +
> + if (virNetDevSetOnline(parentVeth, true) < 0)
> + goto cleanup;
> +
> + if (virNetDevBandwidthSet(net->ifname,
> + virDomainNetGetActualBandwidth(net)) < 0) {
> + lxcError(VIR_ERR_INTERNAL_ERROR,
> + _("cannot set bandwidth limits on %s"),
> + net->ifname);
> + goto cleanup;
> + }
> +
> + if (net->filter &&
> + virDomainConfNWFilterInstantiate(conn, vm->uuid, net) < 0)
> + goto cleanup;
> +
> + ret = 0;
> +
> +cleanup:
> + return ret;
> +}
> +
> +
> +static int lxcSetupInterfaceDirect(virConnectPtr conn,
> + virDomainDefPtr def,
> + virDomainNetDefPtr net,
> + unsigned int *nveths,
> + char ***veths)
> +{
> + int ret = 0;
> + char *res_ifname = NULL;
> + lxc_driver_t *driver = conn->privateData;
> + virNetDevBandwidthPtr bw;
> + virNetDevVPortProfilePtr prof;
> +
> + /* XXX how todo bandwidth controls ?
> + * Since the 'net-ifname' is about to be moved to a different
> + * namespace & renamed, there will be no host side visible
> + * interface for the container to attach rules to
> + */
> + bw = virDomainNetGetActualBandwidth(net);
> + if (bw) {
> + lxcError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
> + _("Unable to set network bandwidth on direct interfaces"));
> + return -1;
> + }
> +
> + /* XXX how todo port profiles ?
> + * Although we can do the association during container
> + * startup, at shutdown we are unable to disassociate
> + * because the macvlan device was moved to the container
> + * and automagically dies when the container dies. So
> + * we have no dev to perform disassociation with.
> + */
> + prof = virDomainNetGetActualVirtPortProfile(net);
> + if (prof) {
> + lxcError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
> + _("Unable to set port profile on direct interfaces"));
> + return -1;
> + }
> +
> + if (VIR_REALLOC_N(*veths, (*nveths)+1) < 0) {
> + virReportOOMError();
> + return -1;
> + }
> + (*veths)[(*nveths)] = NULL;
> +
> + if (virNetDevMacVLanCreateWithVPortProfile(
> + net->ifname, &net->mac,
> + virDomainNetGetActualDirectDev(net),
> + virDomainNetGetActualDirectMode(net),
> + false, false, def->uuid,
> + virDomainNetGetActualVirtPortProfile(net),
> + &res_ifname,
> + VIR_NETDEV_VPORT_PROFILE_OP_CREATE,
> + driver->stateDir,
> + virDomainNetGetActualBandwidth(net)) < 0)
> + goto cleanup;
> +
> + (*veths)[(*nveths)] = res_ifname;
> + (*nveths)++;
> +
> + ret = 0;
> +
> +cleanup:
> + return ret;
> +}
> +
> +
> +/**
> + * lxcSetupInterfaces:
> + * @conn: pointer to connection
> + * @def: pointer to virtual machine structure
> + * @nveths: number of interfaces
> + * @veths: interface names
> + *
> + * Sets up the container interfaces by creating the veth device pairs and
> + * attaching the parent end to the appropriate bridge. The container end
> + * will moved into the container namespace later after clone has been called.
> + *
> + * Returns 0 on success or -1 in case of error
> + */
> +static int lxcSetupInterfaces(virConnectPtr conn,
> + virDomainDefPtr def,
> + unsigned int *nveths,
> + char ***veths)
> +{
> + int ret = -1;
> + size_t i;
> +
> + for (i = 0 ; i < def->nnets ; i++) {
> + /* If appropriate, grab a physical device from the configured
> + * network's pool of devices, or resolve bridge device name
> + * to the one defined in the network definition.
> + */
> + if (networkAllocateActualDevice(def->nets[i]) < 0)
> + goto cleanup;
> +
> + switch (virDomainNetGetActualType(def->nets[i])) {
> + case VIR_DOMAIN_NET_TYPE_NETWORK: {
> + virNetworkPtr network;
> + char *brname = NULL;
> +
> + if (!(network = virNetworkLookupByName(conn,
> + def->nets[i]->data.network.name)))
> + goto cleanup;
> +
> + brname = virNetworkGetBridgeName(network);
> + virNetworkFree(network);
> + if (!brname)
> + goto cleanup;
> +
> + if (lxcSetupInterfaceBridged(conn,
> + def,
> + def->nets[i],
> + brname,
> + nveths,
> + veths) < 0) {
> + VIR_FREE(brname);
> + goto cleanup;
> + }
> + VIR_FREE(brname);
> + break;
> + }
> + case VIR_DOMAIN_NET_TYPE_BRIDGE: {
> + const char *brname = virDomainNetGetActualBridgeName(def->nets[i]);
> + if (!brname) {
> + lxcError(VIR_ERR_INTERNAL_ERROR, "%s",
> + _("No bridge name specified"));
> + goto cleanup;
> + }
> + if (lxcSetupInterfaceBridged(conn,
> + def,
> + def->nets[i],
> + brname,
> + nveths,
> + veths) < 0)
> + goto cleanup;
> + } break;
> +
> + case VIR_DOMAIN_NET_TYPE_DIRECT:
> + if (lxcSetupInterfaceDirect(conn,
> + def,
> + def->nets[i],
> + nveths,
> + veths) < 0)
> + goto cleanup;
> + break;
> +
> + case VIR_DOMAIN_NET_TYPE_USER:
> + case VIR_DOMAIN_NET_TYPE_ETHERNET:
> + case VIR_DOMAIN_NET_TYPE_SERVER:
> + case VIR_DOMAIN_NET_TYPE_CLIENT:
> + case VIR_DOMAIN_NET_TYPE_MCAST:
> + case VIR_DOMAIN_NET_TYPE_INTERNAL:
> + case VIR_DOMAIN_NET_TYPE_LAST:
> + lxcError(VIR_ERR_INTERNAL_ERROR,
> + _("Unsupported network type %s"),
> + virDomainNetTypeToString(
> + virDomainNetGetActualType(def->nets[i])
> + ));
> + goto cleanup;
> + }
> + }
> +
> + ret= 0;
> +
> +cleanup:
> + if (ret != 0) {
> + for (i = 0 ; i < def->nnets ; i++) {
> + virDomainNetDefPtr iface = def->nets[i];
> + virNetDevVPortProfilePtr vport = virDomainNetGetActualVirtPortProfile(iface);
> + if (vport && vport->virtPortType == VIR_NETDEV_VPORT_PROFILE_OPENVSWITCH)
> + ignore_value(virNetDevOpenvswitchRemovePort(
> + virDomainNetGetActualBridgeName(iface),
> + iface->ifname));
> + networkReleaseActualDevice(iface);
> + }
> + }
> + return ret;
> +}
> +
> +
> +static int lxcMonitorClient(lxc_driver_t * driver,
> + virDomainObjPtr vm)
> +{
> + char *sockpath = NULL;
> + int fd = -1;
> + struct sockaddr_un addr;
> +
> + if (virAsprintf(&sockpath, "%s/%s.sock",
> + driver->stateDir, vm->def->name) < 0) {
> + virReportOOMError();
> + return -1;
> + }
> +
> + if (virSecurityManagerSetSocketLabel(driver->securityManager, vm->def) < 0) {
> + VIR_ERROR(_("Failed to set security context for monitor for %s"),
> + vm->def->name);
> + goto error;
> + }
> +
> + fd = socket(PF_UNIX, SOCK_STREAM, 0);
> +
> + if (virSecurityManagerClearSocketLabel(driver->securityManager, vm->def) < 0) {
> + VIR_ERROR(_("Failed to clear security context for monitor for %s"),
> + vm->def->name);
> + goto error;
> + }
> +
> + if (fd < 0) {
> + virReportSystemError(errno, "%s",
> + _("Failed to create client socket"));
> + goto error;
> + }
> +
> + memset(&addr, 0, sizeof(addr));
> + addr.sun_family = AF_UNIX;
> + if (virStrcpyStatic(addr.sun_path, sockpath) == NULL) {
> + lxcError(VIR_ERR_INTERNAL_ERROR,
> + _("Socket path %s too big for destination"), sockpath);
> + goto error;
> + }
> +
> + if (connect(fd, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
> + virReportSystemError(errno, "%s",
> + _("Failed to connect to client socket"));
> + goto error;
> + }
> +
> + VIR_FREE(sockpath);
> + return fd;
> +
> +error:
> + VIR_FREE(sockpath);
> + VIR_FORCE_CLOSE(fd);
> + return -1;
> +}
> +
> +
> +int lxcVmTerminate(lxc_driver_t *driver,
> + virDomainObjPtr vm,
> + virDomainShutoffReason reason)
> +{
> + virCgroupPtr group = NULL;
> + int rc;
> +
> + if (vm->pid <= 0) {
> + lxcError(VIR_ERR_INTERNAL_ERROR,
> + _("Invalid PID %d for container"), vm->pid);
> + return -1;
> + }
> +
> + virSecurityManagerRestoreAllLabel(driver->securityManager,
> + vm->def, false);
> + virSecurityManagerReleaseLabel(driver->securityManager, vm->def);
> + /* Clear out dynamically assigned labels */
> + if (vm->def->seclabel.type == VIR_DOMAIN_SECLABEL_DYNAMIC) {
> + VIR_FREE(vm->def->seclabel.model);
> + VIR_FREE(vm->def->seclabel.label);
> + VIR_FREE(vm->def->seclabel.imagelabel);
> + }
> +
> + if (virCgroupForDomain(driver->cgroup, vm->def->name, &group, 0) == 0) {
> + rc = virCgroupKillPainfully(group);
> + if (rc < 0) {
> + virReportSystemError(-rc, "%s",
> + _("Failed to kill container PIDs"));
> + rc = -1;
> + goto cleanup;
> + }
> + if (rc == 1) {
> + lxcError(VIR_ERR_INTERNAL_ERROR, "%s",
> + _("Some container PIDs refused to die"));
> + rc = -1;
> + goto cleanup;
> + }
> + } else {
> + /* If cgroup doesn't exist, the VM pids must have already
> + * died and so we're just cleaning up stale state
> + */
> + }
> +
> + lxcVmCleanup(driver, vm, reason);
> +
> + rc = 0;
> +
> +cleanup:
> + virCgroupFree(&group);
> + return rc;
> +}
> +
> +extern lxc_driver_t *lxc_driver;
> +static void lxcMonitorEvent(int watch,
> + int fd,
> + int events ATTRIBUTE_UNUSED,
> + void *data)
> +{
> + lxc_driver_t *driver = lxc_driver;
> + virDomainObjPtr vm = data;
> + virDomainEventPtr event = NULL;
> + lxcDomainObjPrivatePtr priv;
> +
> + lxcDriverLock(driver);
> + virDomainObjLock(vm);
> + lxcDriverUnlock(driver);
> +
> + priv = vm->privateData;
> +
> + if (priv->monitor != fd || priv->monitorWatch != watch) {
> + virEventRemoveHandle(watch);
> + goto cleanup;
> + }
> +
> + if (lxcVmTerminate(driver, vm, VIR_DOMAIN_SHUTOFF_SHUTDOWN) < 0) {
> + virEventRemoveHandle(watch);
> + } else {
> + event = virDomainEventNewFromObj(vm,
> + VIR_DOMAIN_EVENT_STOPPED,
> + VIR_DOMAIN_EVENT_STOPPED_SHUTDOWN);
> + virDomainAuditStop(vm, "shutdown");
> + }
> + if (!vm->persistent) {
> + virDomainRemoveInactive(&driver->domains, vm);
> + vm = NULL;
> + }
> +
> +cleanup:
> + if (vm)
> + virDomainObjUnlock(vm);
> + if (event) {
> + lxcDriverLock(driver);
> + virDomainEventStateQueue(driver->domainEventState, event);
> + lxcDriverUnlock(driver);
> + }
> +}
> +
> +
> +static virCommandPtr
> +lxcBuildControllerCmd(lxc_driver_t *driver,
> + virDomainObjPtr vm,
> + int nveths,
> + char **veths,
> + int *ttyFDs,
> + size_t nttyFDs,
> + int handshakefd)
> +{
> + size_t i;
> + char *filterstr;
> + char *outputstr;
> + virCommandPtr cmd;
> +
> + cmd = virCommandNew(vm->def->emulator);
> +
> + /* The controller may call ip command, so we have to retain PATH. */
> + virCommandAddEnvPass(cmd, "PATH");
> +
> + virCommandAddEnvFormat(cmd, "LIBVIRT_DEBUG=%d",
> + virLogGetDefaultPriority());
> +
> + if (virLogGetNbFilters() > 0) {
> + filterstr = virLogGetFilters();
> + if (!filterstr) {
> + virReportOOMError();
> + goto cleanup;
> + }
> +
> + virCommandAddEnvPair(cmd, "LIBVIRT_LOG_FILTERS", filterstr);
> + VIR_FREE(filterstr);
> + }
> +
> + if (driver->log_libvirtd) {
> + if (virLogGetNbOutputs() > 0) {
> + outputstr = virLogGetOutputs();
> + if (!outputstr) {
> + virReportOOMError();
> + goto cleanup;
> + }
> +
> + virCommandAddEnvPair(cmd, "LIBVIRT_LOG_OUTPUTS", outputstr);
> + VIR_FREE(outputstr);
> + }
> + } else {
> + virCommandAddEnvFormat(cmd,
> + "LIBVIRT_LOG_OUTPUTS=%d:stderr",
> + virLogGetDefaultPriority());
> + }
> +
> + virCommandAddArgList(cmd, "--name", vm->def->name, NULL);
> + for (i = 0 ; i < nttyFDs ; i++) {
> + virCommandAddArg(cmd, "--console");
> + virCommandAddArgFormat(cmd, "%d", ttyFDs[i]);
> + virCommandPreserveFD(cmd, ttyFDs[i]);
> + }
> +
> + virCommandAddArgPair(cmd, "--security",
> + virSecurityManagerGetModel(driver->securityManager));
> +
> + virCommandAddArg(cmd, "--handshake");
> + virCommandAddArgFormat(cmd, "%d", handshakefd);
> + virCommandAddArg(cmd, "--background");
> +
> + for (i = 0 ; i < nveths ; i++) {
> + virCommandAddArgList(cmd, "--veth", veths[i], NULL);
> + }
> +
> + virCommandPreserveFD(cmd, handshakefd);
> +
> + return cmd;
> +cleanup:
> + virCommandFree(cmd);
> + return NULL;
> +}
> +
> +static int
> +lxcReadLogOutput(virDomainObjPtr vm,
> + char *logfile,
> + off_t pos,
> + char *buf,
> + size_t buflen)
> +{
> + int fd;
> + off_t off;
> + int whence;
> + int got = 0, ret = -1;
> + int retries = 10;
> +
> + if ((fd = open(logfile, O_RDONLY)) < 0) {
> + virReportSystemError(errno, _("failed to open logfile %s"),
> + logfile);
> + goto cleanup;
> + }
> +
> + if (pos < 0) {
> + off = 0;
> + whence = SEEK_END;
> + } else {
> + off = pos;
> + whence = SEEK_SET;
> + }
> +
> + if (lseek(fd, off, whence) < 0) {
> + if (whence == SEEK_END)
> + virReportSystemError(errno,
> + _("unable to seek to end of log for %s"),
> + logfile);
> + else
> + virReportSystemError(errno,
> + _("unable to seek to %lld from start for %s"),
> + (long long)off, logfile);
> + goto cleanup;
> + }
> +
> + while (retries) {
> + ssize_t bytes;
> + int isdead = 0;
> +
> + if (kill(vm->pid, 0) == -1 && errno == ESRCH)
> + isdead = 1;
> +
> + /* Any failures should be detected before we read the log, so we
> + * always have something useful to report on failure. */
> + bytes = saferead(fd, buf+got, buflen-got-1);
> + if (bytes < 0) {
> + virReportSystemError(errno, "%s",
> + _("Failure while reading guest log output"));
> + goto cleanup;
> + }
> +
> + got += bytes;
> + buf[got] = '\0';
> +
> + if ((got == buflen-1) || isdead) {
> + break;
> + }
> +
> + usleep(100*1000);
> + retries--;
> + }
> +
> +
> + ret = got;
> +cleanup:
> + VIR_FORCE_CLOSE(fd);
> + return ret;
> +}
> +
> +/**
> + * lxcVmStart:
> + * @conn: pointer to connection
> + * @driver: pointer to driver structure
> + * @vm: pointer to virtual machine structure
> + * @autoDestroy: mark the domain for auto destruction
> + * @reason: reason for switching vm to running state
> + *
> + * Starts a vm
> + *
> + * Returns 0 on success or -1 in case of error
> + */
> +int lxcVmStart(virConnectPtr conn,
> + lxc_driver_t * driver,
> + virDomainObjPtr vm,
> + bool autoDestroy,
> + virDomainRunningReason reason)
> +{
> + int rc = -1, r;
> + size_t nttyFDs = 0;
> + int *ttyFDs = NULL;
> + size_t i;
> + char *logfile = NULL;
> + int logfd = -1;
> + unsigned int nveths = 0;
> + char **veths = NULL;
> + int handshakefds[2] = { -1, -1 };
> + off_t pos = -1;
> + char ebuf[1024];
> + char *timestamp;
> + virCommandPtr cmd = NULL;
> + lxcDomainObjPrivatePtr priv = vm->privateData;
> + virErrorPtr err = NULL;
> +
> + if (!lxc_driver->cgroup) {
> + lxcError(VIR_ERR_INTERNAL_ERROR, "%s",
> + _("The 'cpuacct', 'devices' & 'memory' cgroups controllers must be mounted"));
> + return -1;
> + }
> +
> + if (!virCgroupMounted(lxc_driver->cgroup,
> + VIR_CGROUP_CONTROLLER_CPUACCT)) {
> + lxcError(VIR_ERR_INTERNAL_ERROR, "%s",
> + _("Unable to find 'cpuacct' cgroups controller mount"));
> + return -1;
> + }
> + if (!virCgroupMounted(lxc_driver->cgroup,
> + VIR_CGROUP_CONTROLLER_DEVICES)) {
> + lxcError(VIR_ERR_INTERNAL_ERROR, "%s",
> + _("Unable to find 'devices' cgroups controller mount"));
> + return -1;
> + }
> + if (!virCgroupMounted(lxc_driver->cgroup,
> + VIR_CGROUP_CONTROLLER_MEMORY)) {
> + lxcError(VIR_ERR_INTERNAL_ERROR, "%s",
> + _("Unable to find 'memory' cgroups controller mount"));
> + return -1;
> + }
> +
> + if (virFileMakePath(driver->logDir) < 0) {
> + virReportSystemError(errno,
> + _("Cannot create log directory '%s'"),
> + driver->logDir);
> + return -1;
> + }
> +
> + if (virAsprintf(&logfile, "%s/%s.log",
> + driver->logDir, vm->def->name) < 0) {
> + virReportOOMError();
> + return -1;
> + }
> +
> + /* Do this up front, so any part of the startup process can add
> + * runtime state to vm->def that won't be persisted. This let's us
> + * report implicit runtime defaults in the XML, like vnc listen/socket
> + */
> + VIR_DEBUG("Setting current domain def as transient");
> + if (virDomainObjSetDefTransient(driver->caps, vm, true) < 0)
> + goto cleanup;
> +
> + /* Run an early hook to set-up missing devices */
> + if (virHookPresent(VIR_HOOK_DRIVER_LXC)) {
> + char *xml = virDomainDefFormat(vm->def, 0);
> + int hookret;
> +
> + hookret = virHookCall(VIR_HOOK_DRIVER_LXC, vm->def->name,
> + VIR_HOOK_LXC_OP_PREPARE, VIR_HOOK_SUBOP_BEGIN,
> + NULL, xml, NULL);
> + VIR_FREE(xml);
> +
> + /*
> + * If the script raised an error abort the launch
> + */
> + if (hookret < 0)
> + goto cleanup;
> + }
> +
> + /* Here we open all the PTYs we need on the host OS side.
> + * The LXC controller will open the guest OS side PTYs
> + * and forward I/O between them.
> + */
> + nttyFDs = vm->def->nconsoles;
> + if (VIR_ALLOC_N(ttyFDs, nttyFDs) < 0) {
> + virReportOOMError();
> + goto cleanup;
> + }
> +
> + /* If you are using a SecurityDriver with dynamic labelling,
> + then generate a security label for isolation */
> + VIR_DEBUG("Generating domain security label (if required)");
> + if (vm->def->seclabel.type == VIR_DOMAIN_SECLABEL_DEFAULT)
> + vm->def->seclabel.type = VIR_DOMAIN_SECLABEL_NONE;
> +
> + if (virSecurityManagerGenLabel(driver->securityManager, vm->def) < 0) {
> + virDomainAuditSecurityLabel(vm, false);
> + goto cleanup;
> + }
> + virDomainAuditSecurityLabel(vm, true);
> +
> + VIR_DEBUG("Setting domain security labels");
> + if (virSecurityManagerSetAllLabel(driver->securityManager,
> + vm->def, NULL) < 0)
> + goto cleanup;
> +
> + for (i = 0 ; i < vm->def->nconsoles ; i++)
> + ttyFDs[i] = -1;
> +
> + for (i = 0 ; i < vm->def->nconsoles ; i++) {
> + char *ttyPath;
> + if (vm->def->consoles[i]->source.type != VIR_DOMAIN_CHR_TYPE_PTY) {
> + lxcError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
> + _("Only PTY console types are supported"));
> + goto cleanup;
> + }
> +
> + if (virFileOpenTty(&ttyFDs[i], &ttyPath, 1) < 0) {
> + virReportSystemError(errno, "%s",
> + _("Failed to allocate tty"));
> + goto cleanup;
> + }
> +
> + VIR_FREE(vm->def->consoles[i]->source.data.file.path);
> + vm->def->consoles[i]->source.data.file.path = ttyPath;
> +
> + VIR_FREE(vm->def->consoles[i]->info.alias);
> + if (virAsprintf(&vm->def->consoles[i]->info.alias, "console%zu", i) < 0) {
> + virReportOOMError();
> + goto cleanup;
> + }
> + }
> +
> + if (lxcSetupInterfaces(conn, vm->def, &nveths, &veths) != 0)
> + goto cleanup;
> +
> + /* Save the configuration for the controller */
> + if (virDomainSaveConfig(driver->stateDir, vm->def) < 0)
> + goto cleanup;
> +
> + if ((logfd = open(logfile, O_WRONLY | O_APPEND | O_CREAT,
> + S_IRUSR|S_IWUSR)) < 0) {
> + virReportSystemError(errno,
> + _("Failed to open '%s'"),
> + logfile);
> + goto cleanup;
> + }
> +
> + if (pipe(handshakefds) < 0) {
> + virReportSystemError(errno, "%s",
> + _("Unable to create pipe"));
> + goto cleanup;
> + }
> +
> + if (!(cmd = lxcBuildControllerCmd(driver,
> + vm,
> + nveths, veths,
> + ttyFDs, nttyFDs,
> + handshakefds[1])))
> + goto cleanup;
> + virCommandSetOutputFD(cmd, &logfd);
> + virCommandSetErrorFD(cmd, &logfd);
> +
> + /* now that we know it is about to start call the hook if present */
> + if (virHookPresent(VIR_HOOK_DRIVER_LXC)) {
> + char *xml = virDomainDefFormat(vm->def, 0);
> + int hookret;
> +
> + hookret = virHookCall(VIR_HOOK_DRIVER_LXC, vm->def->name,
> + VIR_HOOK_LXC_OP_START, VIR_HOOK_SUBOP_BEGIN,
> + NULL, xml, NULL);
> + VIR_FREE(xml);
> +
> + /*
> + * If the script raised an error abort the launch
> + */
> + if (hookret < 0)
> + goto cleanup;
> + }
> +
> + /* Log timestamp */
> + if ((timestamp = virTimeStringNow()) == NULL) {
> + virReportOOMError();
> + goto cleanup;
> + }
> + if (safewrite(logfd, timestamp, strlen(timestamp)) < 0 ||
> + safewrite(logfd, START_POSTFIX, strlen(START_POSTFIX)) < 0) {
> + VIR_WARN("Unable to write timestamp to logfile: %s",
> + virStrerror(errno, ebuf, sizeof(ebuf)));
> + }
> + VIR_FREE(timestamp);
> +
> + /* Log generated command line */
> + virCommandWriteArgLog(cmd, logfd);
> + if ((pos = lseek(logfd, 0, SEEK_END)) < 0)
> + VIR_WARN("Unable to seek to end of logfile: %s",
> + virStrerror(errno, ebuf, sizeof(ebuf)));
> +
> + if (virCommandRun(cmd, NULL) < 0)
> + goto cleanup;
> +
> + if (VIR_CLOSE(handshakefds[1]) < 0) {
> + virReportSystemError(errno, "%s", _("could not close handshake fd"));
> + goto cleanup;
> + }
> +
> + /* Connect to the controller as a client *first* because
> + * this will block until the child has written their
> + * pid file out to disk */
> + if ((priv->monitor = lxcMonitorClient(driver, vm)) < 0)
> + goto cleanup;
> +
> + /* And get its pid */
> + if ((r = virPidFileRead(driver->stateDir, vm->def->name, &vm->pid)) < 0) {
> + virReportSystemError(-r,
> + _("Failed to read pid file %s/%s.pid"),
> + driver->stateDir, vm->def->name);
> + goto cleanup;
> + }
> +
> + vm->def->id = vm->pid;
> + virDomainObjSetState(vm, VIR_DOMAIN_RUNNING, reason);
> +
> + if (lxcContainerWaitForContinue(handshakefds[0]) < 0) {
> + char out[1024];
> +
> + if (!(lxcReadLogOutput(vm, logfile, pos, out, 1024) < 0)) {
> + lxcError(VIR_ERR_INTERNAL_ERROR,
> + _("guest failed to start: %s"), out);
> + }
> +
> + goto error;
> + }
> +
> + if ((priv->monitorWatch = virEventAddHandle(
> + priv->monitor,
> + VIR_EVENT_HANDLE_ERROR | VIR_EVENT_HANDLE_HANGUP,
> + lxcMonitorEvent,
> + vm, NULL)) < 0) {
> + goto error;
> + }
> +
> + if (autoDestroy &&
> + lxcProcessAutoDestroyAdd(driver, vm, conn) < 0)
> + goto error;
> +
> + if (virDomainObjSetDefTransient(driver->caps, vm, false) < 0)
> + goto error;
> +
> + /* Write domain status to disk.
> + *
> + * XXX: Earlier we wrote the plain "live" domain XML to this
> + * location for the benefit of libvirt_lxc. We're now overwriting
> + * it with the live status XML instead. This is a (currently
> + * harmless) inconsistency we should fix one day */
> + if (virDomainSaveStatus(driver->caps, driver->stateDir, vm) < 0)
> + goto error;
> +
> + /* finally we can call the 'started' hook script if any */
> + if (virHookPresent(VIR_HOOK_DRIVER_LXC)) {
> + char *xml = virDomainDefFormat(vm->def, 0);
> + int hookret;
> +
> + hookret = virHookCall(VIR_HOOK_DRIVER_LXC, vm->def->name,
> + VIR_HOOK_LXC_OP_STARTED, VIR_HOOK_SUBOP_BEGIN,
> + NULL, xml, NULL);
> + VIR_FREE(xml);
> +
> + /*
> + * If the script raised an error abort the launch
> + */
> + if (hookret < 0)
> + goto error;
> + }
> +
> + rc = 0;
> +
> +cleanup:
> + if (rc != 0 && !err)
> + err = virSaveLastError();
> + virCommandFree(cmd);
> + if (VIR_CLOSE(logfd) < 0) {
> + virReportSystemError(errno, "%s", _("could not close logfile"));
> + rc = -1;
> + }
> + for (i = 0 ; i < nveths ; i++) {
> + if (rc != 0)
> + ignore_value(virNetDevVethDelete(veths[i]));
> + VIR_FREE(veths[i]);
> + }
> + if (rc != 0) {
> + VIR_FORCE_CLOSE(priv->monitor);
> + virDomainConfVMNWFilterTeardown(vm);
> +
> + virSecurityManagerRestoreAllLabel(driver->securityManager,
> + vm->def, false);
> + virSecurityManagerReleaseLabel(driver->securityManager, vm->def);
> + /* Clear out dynamically assigned labels */
> + if (vm->def->seclabel.type == VIR_DOMAIN_SECLABEL_DYNAMIC) {
> + VIR_FREE(vm->def->seclabel.model);
> + VIR_FREE(vm->def->seclabel.label);
> + VIR_FREE(vm->def->seclabel.imagelabel);
> + }
> + }
> + for (i = 0 ; i < nttyFDs ; i++)
> + VIR_FORCE_CLOSE(ttyFDs[i]);
> + VIR_FREE(ttyFDs);
> + VIR_FORCE_CLOSE(handshakefds[0]);
> + VIR_FORCE_CLOSE(handshakefds[1]);
> + VIR_FREE(logfile);
> +
> + if (err) {
> + virSetError(err);
> + virFreeError(err);
> + }
> +
> + return rc;
> +
> +error:
> + err = virSaveLastError();
> + lxcVmTerminate(driver, vm, VIR_DOMAIN_SHUTOFF_FAILED);
> + goto cleanup;
> +}
> +
> +struct lxcAutostartData {
> + lxc_driver_t *driver;
> + virConnectPtr conn;
> +};
> +
> +static void
> +lxcAutostartDomain(void *payload, const void *name ATTRIBUTE_UNUSED, void *opaque)
> +{
> + virDomainObjPtr vm = payload;
> + const struct lxcAutostartData *data = opaque;
> +
> + virDomainObjLock(vm);
> + if (vm->autostart &&
> + !virDomainObjIsActive(vm)) {
> + int ret = lxcVmStart(data->conn, data->driver, vm, false,
> + VIR_DOMAIN_RUNNING_BOOTED);
> + virDomainAuditStart(vm, "booted", ret >= 0);
> + if (ret < 0) {
> + virErrorPtr err = virGetLastError();
> + VIR_ERROR(_("Failed to autostart VM '%s': %s"),
> + vm->def->name,
> + err ? err->message : "");
> + } else {
> + virDomainEventPtr event =
> + virDomainEventNewFromObj(vm,
> + VIR_DOMAIN_EVENT_STARTED,
> + VIR_DOMAIN_EVENT_STARTED_BOOTED);
> + if (event)
> + virDomainEventStateQueue(data->driver->domainEventState, event);
> + }
> + }
> + virDomainObjUnlock(vm);
> +}
> +
> +
> +void
> +lxcAutostartConfigs(lxc_driver_t *driver) {
> + /* XXX: Figure out a better way todo this. The domain
> + * startup code needs a connection handle in order
> + * to lookup the bridge associated with a virtual
> + * network
> + */
> + virConnectPtr conn = virConnectOpen("lxc:///");
> + /* Ignoring NULL conn which is mostly harmless here */
> +
> + struct lxcAutostartData data = { driver, conn };
> +
> + lxcDriverLock(driver);
> + virHashForEach(driver->domains.objs, lxcAutostartDomain, &data);
> + lxcDriverUnlock(driver);
> +
> + if (conn)
> + virConnectClose(conn);
> +}
> +
> +static void
> +lxcReconnectVM(void *payload, const void *name ATTRIBUTE_UNUSED, void *opaque)
> +{
> + virDomainObjPtr vm = payload;
> + lxc_driver_t *driver = opaque;
> + lxcDomainObjPrivatePtr priv;
> +
> + virDomainObjLock(vm);
> + VIR_DEBUG("Reconnect %d %d %d\n", vm->def->id, vm->pid, vm->state.state);
> +
> + priv = vm->privateData;
> +
> + if (vm->pid != 0) {
> + vm->def->id = vm->pid;
> + virDomainObjSetState(vm, VIR_DOMAIN_RUNNING,
> + VIR_DOMAIN_RUNNING_UNKNOWN);
> +
> + if ((priv->monitor = lxcMonitorClient(driver, vm)) < 0)
> + goto error;
> +
> + if ((priv->monitorWatch = virEventAddHandle(
> + priv->monitor,
> + VIR_EVENT_HANDLE_ERROR | VIR_EVENT_HANDLE_HANGUP,
> + lxcMonitorEvent,
> + vm, NULL)) < 0)
> + goto error;
> +
> + if (virSecurityManagerReserveLabel(driver->securityManager,
> + vm->def, vm->pid) < 0)
> + goto error;
> +
> + /* now that we know it's reconnected call the hook if present */
> + if (virHookPresent(VIR_HOOK_DRIVER_LXC)) {
> + char *xml = virDomainDefFormat(vm->def, 0);
> + int hookret;
> +
> + /* we can't stop the operation even if the script raised an error */
> + hookret = virHookCall(VIR_HOOK_DRIVER_LXC, vm->def->name,
> + VIR_HOOK_LXC_OP_RECONNECT, VIR_HOOK_SUBOP_BEGIN,
> + NULL, xml, NULL);
> + VIR_FREE(xml);
> + if (hookret < 0)
> + goto error;
> + }
> +
> + } else {
> + vm->def->id = -1;
> + VIR_FORCE_CLOSE(priv->monitor);
> + }
> +
> +cleanup:
> + virDomainObjUnlock(vm);
> + return;
> +
> +error:
> + lxcVmTerminate(driver, vm, VIR_DOMAIN_SHUTOFF_FAILED);
> + virDomainAuditStop(vm, "failed");
> + goto cleanup;
> +}
> +
> +
> +int lxcReconnectAll(lxc_driver_t *driver,
> + virDomainObjListPtr doms)
> +{
> + virHashForEach(doms->objs, lxcReconnectVM, driver);
> + return 0;
> +}
> diff --git a/src/lxc/lxc_process.h b/src/lxc/lxc_process.h
> new file mode 100644
> index 0000000..b4b707b
> --- /dev/null
> +++ b/src/lxc/lxc_process.h
> @@ -0,0 +1,49 @@
> +/*
> + * Copyright (C) 2010-2012 Red Hat, Inc.
> + * Copyright IBM Corp. 2008
> + *
> + * lxc_process.h: LXC process lifecycle management
> + *
> + * 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
> + */
> +
> +#ifndef __LXC_PROCESS_H__
> +# define __LXC_PROCESS_H__
> +
> +# include "lxc_conf.h"
> +
> +int lxcVmStart(virConnectPtr conn,
> + lxc_driver_t * driver,
> + virDomainObjPtr vm,
> + bool autoDestroy,
> + virDomainRunningReason reason);
> +int lxcVmTerminate(lxc_driver_t *driver,
> + virDomainObjPtr vm,
> + virDomainShutoffReason reason);
> +int lxcProcessAutoDestroyInit(lxc_driver_t *driver);
> +void lxcProcessAutoDestroyRun(lxc_driver_t *driver,
> + virConnectPtr conn);
> +void lxcProcessAutoDestroyShutdown(lxc_driver_t *driver);
> +int lxcProcessAutoDestroyAdd(lxc_driver_t *driver,
> + virDomainObjPtr vm,
> + virConnectPtr conn);
> +int lxcProcessAutoDestroyRemove(lxc_driver_t *driver,
> + virDomainObjPtr vm);
> +
> +void lxcAutostartConfigs(lxc_driver_t *driver);
> +int lxcReconnectAll(lxc_driver_t *driver,
> + virDomainObjListPtr doms);
> +
> +#endif /* __LXC_PROCESS_H__ */
Looks okay, ACK, but diff really ain't our friend for those.
Daniel
--
Daniel Veillard | libxml Gnome XML XSLT toolkit http://xmlsoft.org/
daniel at veillard.com | Rpmfind RPM search engine http://rpmfind.net/
http://veillard.com/ | virtualization library http://libvirt.org/
More information about the libvir-list
mailing list