2012-07-13 11:39:29 +00:00
|
|
|
/*
|
2016-05-04 17:18:16 +00:00
|
|
|
* Copyright (C) 2010-2016 Red Hat, Inc.
|
2012-07-13 11:39:29 +00:00
|
|
|
* 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
|
2012-09-20 22:30:55 +00:00
|
|
|
* License along with this library. If not, see
|
2012-07-21 10:06:23 +00:00
|
|
|
* <http://www.gnu.org/licenses/>.
|
2012-07-13 11:39:29 +00:00
|
|
|
*/
|
|
|
|
|
|
|
|
#include <config.h>
|
|
|
|
|
2013-03-08 16:28:37 +00:00
|
|
|
#include <sys/stat.h>
|
2012-07-13 11:39:29 +00:00
|
|
|
#include <unistd.h>
|
|
|
|
#include <fcntl.h>
|
|
|
|
#include <signal.h>
|
|
|
|
|
|
|
|
#include "lxc_process.h"
|
|
|
|
#include "lxc_domain.h"
|
|
|
|
#include "lxc_container.h"
|
2013-03-21 14:40:29 +00:00
|
|
|
#include "lxc_cgroup.h"
|
2012-11-14 09:39:04 +00:00
|
|
|
#include "lxc_fuse.h"
|
2012-07-13 11:39:29 +00:00
|
|
|
#include "datatypes.h"
|
|
|
|
#include "virfile.h"
|
|
|
|
#include "virpidfile.h"
|
|
|
|
#include "virnetdev.h"
|
|
|
|
#include "virnetdevveth.h"
|
|
|
|
#include "virnetdevbridge.h"
|
2015-03-17 17:46:44 +00:00
|
|
|
#include "virnetdevopenvswitch.h"
|
2012-07-13 11:39:29 +00:00
|
|
|
#include "virtime.h"
|
|
|
|
#include "domain_nwfilter.h"
|
|
|
|
#include "network/bridge_driver.h"
|
2012-12-12 18:06:53 +00:00
|
|
|
#include "viralloc.h"
|
2012-07-13 11:39:29 +00:00
|
|
|
#include "domain_audit.h"
|
2012-12-13 18:21:53 +00:00
|
|
|
#include "virerror.h"
|
2012-12-12 17:59:27 +00:00
|
|
|
#include "virlog.h"
|
2012-12-12 16:27:01 +00:00
|
|
|
#include "vircommand.h"
|
2012-11-23 14:46:18 +00:00
|
|
|
#include "lxc_hostdev.h"
|
2012-12-12 17:00:34 +00:00
|
|
|
#include "virhook.h"
|
2013-04-03 10:36:23 +00:00
|
|
|
#include "virstring.h"
|
2013-07-16 17:05:06 +00:00
|
|
|
#include "viratomic.h"
|
2013-07-22 15:36:06 +00:00
|
|
|
#include "virprocess.h"
|
2013-09-30 14:46:29 +00:00
|
|
|
#include "virsystemd.h"
|
2014-11-18 23:55:48 +00:00
|
|
|
#include "netdev_bandwidth_conf.h"
|
2012-07-13 11:39:29 +00:00
|
|
|
|
|
|
|
#define VIR_FROM_THIS VIR_FROM_LXC
|
|
|
|
|
2014-02-28 12:16:17 +00:00
|
|
|
VIR_LOG_INIT("lxc.lxc_process");
|
|
|
|
|
2012-07-13 11:39:29 +00:00
|
|
|
#define START_POSTFIX ": starting up\n"
|
|
|
|
|
2013-07-15 17:08:11 +00:00
|
|
|
static virDomainObjPtr
|
|
|
|
lxcProcessAutoDestroy(virDomainObjPtr dom,
|
|
|
|
virConnectPtr conn,
|
|
|
|
void *opaque)
|
2012-07-13 11:39:29 +00:00
|
|
|
{
|
2013-07-15 17:08:11 +00:00
|
|
|
virLXCDriverPtr driver = opaque;
|
2013-11-22 14:38:05 +00:00
|
|
|
virObjectEventPtr event = NULL;
|
2012-07-17 10:55:38 +00:00
|
|
|
virLXCDomainObjPrivatePtr priv;
|
2012-07-13 11:39:29 +00:00
|
|
|
|
2013-07-15 17:08:11 +00:00
|
|
|
VIR_DEBUG("driver=%p dom=%s conn=%p", driver, dom->def->name, conn);
|
2012-07-13 11:39:29 +00:00
|
|
|
|
2012-07-17 10:55:38 +00:00
|
|
|
priv = dom->privateData;
|
2012-07-13 11:39:29 +00:00
|
|
|
VIR_DEBUG("Killing domain");
|
2013-07-15 17:08:11 +00:00
|
|
|
virLXCProcessStop(driver, dom, VIR_DOMAIN_SHUTOFF_DESTROYED);
|
2012-07-13 11:39:29 +00:00
|
|
|
virDomainAuditStop(dom, "destroyed");
|
2013-11-21 17:03:26 +00:00
|
|
|
event = virDomainEventLifecycleNewFromObj(dom,
|
2012-07-13 11:39:29 +00:00
|
|
|
VIR_DOMAIN_EVENT_STOPPED,
|
|
|
|
VIR_DOMAIN_EVENT_STOPPED_DESTROYED);
|
2012-07-17 10:55:38 +00:00
|
|
|
priv->doneStopEvent = true;
|
2012-07-13 11:39:29 +00:00
|
|
|
|
2013-07-22 20:59:41 +00:00
|
|
|
if (!dom->persistent) {
|
2013-07-15 17:08:11 +00:00
|
|
|
virDomainObjListRemove(driver->domains, dom);
|
|
|
|
dom = NULL;
|
|
|
|
}
|
2012-07-13 11:39:29 +00:00
|
|
|
|
|
|
|
if (event)
|
2013-11-21 10:43:10 +00:00
|
|
|
virObjectEventStateQueue(driver->domainEventState, event);
|
2013-07-15 17:08:11 +00:00
|
|
|
|
|
|
|
return dom;
|
2012-07-13 11:39:29 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Precondition: driver is locked
|
|
|
|
*/
|
2012-07-23 11:25:37 +00:00
|
|
|
static int
|
|
|
|
virLXCProcessReboot(virLXCDriverPtr driver,
|
|
|
|
virDomainObjPtr vm)
|
|
|
|
{
|
2013-07-15 17:08:11 +00:00
|
|
|
virConnectPtr conn = virCloseCallbacksGetConn(driver->closeCallbacks, vm);
|
2012-07-23 11:25:37 +00:00
|
|
|
int reason = vm->state.reason;
|
|
|
|
bool autodestroy = false;
|
|
|
|
int ret = -1;
|
|
|
|
virDomainDefPtr savedDef;
|
|
|
|
|
Fix deadlock in handling EOF in LXC monitor
Depending on the scenario in which LXC containers exit, it is
possible for the EOF callback of the LXC monitor to deadlock
the driver.
#0 0x00000038a0a0de4d in __lll_lock_wait () from /lib64/libpthread.so.0
#1 0x00000038a0a09ca6 in _L_lock_840 () from /lib64/libpthread.so.0
#2 0x00000038a0a09ba8 in pthread_mutex_lock () from /lib64/libpthread.so.0
#3 0x00007f4bd9579d55 in virMutexLock (m=<optimized out>) at util/threads-pthread.c:85
#4 0x00007f4bcacc7597 in lxcDriverLock (driver=0x7f4bc40c8290) at lxc/lxc_conf.h:81
#5 virLXCProcessMonitorEOFNotify (mon=<optimized out>, vm=0x7f4bb4000b00) at lxc/lxc_process.c:581
#6 0x00007f4bd9645c91 in virNetClientCloseLocked (client=client@entry=0x7f4bb4009e60)
at rpc/virnetclient.c:554
#7 0x00007f4bd96460f8 in virNetClientIOEventLoopPassTheBuck (thiscall=0x0, client=0x7f4bb4009e60)
at rpc/virnetclient.c:1306
#8 virNetClientIOEventLoopPassTheBuck (client=0x7f4bb4009e60, thiscall=0x0)
at rpc/virnetclient.c:1287
#9 0x00007f4bd96467a2 in virNetClientCloseInternal (reason=3, client=0x7f4bb4009e60)
at rpc/virnetclient.c:589
#10 virNetClientCloseInternal (client=0x7f4bb4009e60, reason=3) at rpc/virnetclient.c:561
#11 0x00007f4bcacc4a82 in virLXCMonitorClose (mon=0x7f4bb4000a00) at lxc/lxc_monitor.c:201
#12 0x00007f4bcacc55ac in virLXCProcessCleanup (reason=<optimized out>, vm=0x7f4bb4000b00,
driver=0x7f4bc40c8290) at lxc/lxc_process.c:240
#13 virLXCProcessStop (driver=0x7f4bc40c8290, vm=vm@entry=0x7f4bb4000b00,
reason=reason@entry=VIR_DOMAIN_SHUTOFF_DESTROYED) at lxc/lxc_process.c:735
#14 0x00007f4bcacc5bd2 in virLXCProcessAutoDestroyDom (payload=<optimized out>,
name=0x7f4bb4003c80, opaque=0x7fff41af2df0) at lxc/lxc_process.c:94
#15 0x00007f4bd9586649 in virHashForEach (table=0x7f4bc409b270,
iter=iter@entry=0x7f4bcacc5ab0 <virLXCProcessAutoDestroyDom>, data=data@entry=0x7fff41af2df0)
at util/virhash.c:514
#16 0x00007f4bcacc52d7 in virLXCProcessAutoDestroyRun (driver=driver@entry=0x7f4bc40c8290,
conn=conn@entry=0x7f4bb8000ab0) at lxc/lxc_process.c:120
#17 0x00007f4bcacca628 in lxcClose (conn=0x7f4bb8000ab0) at lxc/lxc_driver.c:128
#18 0x00007f4bd95e67ab in virReleaseConnect (conn=conn@entry=0x7f4bb8000ab0) at datatypes.c:114
When the driver calls virLXCMonitorClose, there is really no
need for the EOF callback to be invoked in this case, since
the caller can easily handle events itself. In changing this,
the monitor needs to take a deep copy of the callback list,
not merely a reference.
Also adds debug statements in various places to aid
troubleshooting
Signed-off-by: Daniel P. Berrange <berrange@redhat.com>
2012-09-26 14:08:20 +00:00
|
|
|
VIR_DEBUG("Faking reboot");
|
|
|
|
|
2012-07-23 11:25:37 +00:00
|
|
|
if (conn) {
|
2014-01-03 15:08:52 +00:00
|
|
|
virObjectRef(conn);
|
2012-07-23 11:25:37 +00:00
|
|
|
autodestroy = true;
|
|
|
|
} else {
|
|
|
|
conn = virConnectOpen("lxc:///");
|
|
|
|
/* Ignoring NULL conn which is mostly harmless here */
|
|
|
|
}
|
|
|
|
|
|
|
|
/* In a reboot scenario, we need to make sure we continue
|
|
|
|
* to use the current 'def', and not switch to 'newDef'.
|
|
|
|
* So temporarily hide the newDef and then reinstate it
|
|
|
|
*/
|
|
|
|
savedDef = vm->newDef;
|
|
|
|
vm->newDef = NULL;
|
|
|
|
virLXCProcessStop(driver, vm, VIR_DOMAIN_SHUTOFF_SHUTDOWN);
|
|
|
|
vm->newDef = savedDef;
|
2013-07-09 17:15:45 +00:00
|
|
|
if (virLXCProcessStart(conn, driver, vm,
|
|
|
|
0, NULL, autodestroy, reason) < 0) {
|
2012-07-23 11:25:37 +00:00
|
|
|
VIR_WARN("Unable to handle reboot of vm %s",
|
|
|
|
vm->def->name);
|
|
|
|
goto cleanup;
|
|
|
|
}
|
|
|
|
|
|
|
|
ret = 0;
|
|
|
|
|
2014-03-25 06:49:26 +00:00
|
|
|
cleanup:
|
2014-01-03 15:08:52 +00:00
|
|
|
virObjectUnref(conn);
|
2012-07-23 11:25:37 +00:00
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2012-07-13 11:39:29 +00:00
|
|
|
|
2015-07-28 16:25:59 +00:00
|
|
|
static void
|
|
|
|
lxcProcessRemoveDomainStatus(virLXCDriverConfigPtr cfg,
|
|
|
|
virDomainObjPtr vm)
|
|
|
|
{
|
|
|
|
char ebuf[1024];
|
|
|
|
char *file = NULL;
|
|
|
|
|
|
|
|
if (virAsprintf(&file, "%s/%s.xml", cfg->stateDir, vm->def->name) < 0)
|
|
|
|
return;
|
|
|
|
|
|
|
|
if (unlink(file) < 0 && errno != ENOENT && errno != ENOTDIR)
|
|
|
|
VIR_WARN("Failed to remove domain XML for %s: %s",
|
|
|
|
vm->def->name, virStrerror(errno, ebuf, sizeof(ebuf)));
|
|
|
|
VIR_FREE(file);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2012-07-13 11:39:29 +00:00
|
|
|
/**
|
2012-07-13 11:49:24 +00:00
|
|
|
* virLXCProcessCleanup:
|
2012-07-13 11:39:29 +00:00
|
|
|
* @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
|
|
|
|
*
|
|
|
|
*/
|
2012-07-13 11:56:29 +00:00
|
|
|
static void virLXCProcessCleanup(virLXCDriverPtr driver,
|
2012-07-13 11:49:24 +00:00
|
|
|
virDomainObjPtr vm,
|
|
|
|
virDomainShutoffReason reason)
|
2012-07-13 11:39:29 +00:00
|
|
|
{
|
Convert 'int i' to 'size_t i' in src/lxc/ files
Convert the type of loop iterators named 'i', 'j', k',
'ii', 'jj', 'kk', to be 'size_t' instead of 'int' or
'unsigned int', also santizing 'ii', 'jj', 'kk' to use
the normal 'i', 'j', 'k' naming
Signed-off-by: Daniel P. Berrange <berrange@redhat.com>
2013-07-08 14:09:33 +00:00
|
|
|
size_t i;
|
2012-07-18 16:20:46 +00:00
|
|
|
virLXCDomainObjPrivatePtr priv = vm->privateData;
|
2012-07-13 11:39:29 +00:00
|
|
|
virNetDevVPortProfilePtr vport = NULL;
|
2013-07-16 15:45:05 +00:00
|
|
|
virLXCDriverConfigPtr cfg = virLXCDriverGetConfig(driver);
|
2012-07-13 11:39:29 +00:00
|
|
|
|
2015-02-12 19:32:39 +00:00
|
|
|
VIR_DEBUG("Cleanup VM name=%s pid=%d reason=%d",
|
2012-07-17 14:54:08 +00:00
|
|
|
vm->def->name, (int)vm->pid, (int)reason);
|
|
|
|
|
2012-07-13 11:39:29 +00:00
|
|
|
/* now that we know it's stopped call the hook if present */
|
|
|
|
if (virHookPresent(VIR_HOOK_DRIVER_LXC)) {
|
2016-02-03 21:40:35 +00:00
|
|
|
char *xml = virDomainDefFormat(vm->def, driver->caps, 0);
|
2012-07-13 11:39:29 +00:00
|
|
|
|
|
|
|
/* 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 */
|
2013-07-15 17:08:11 +00:00
|
|
|
virCloseCallbacksUnset(driver->closeCallbacks, vm,
|
|
|
|
lxcProcessAutoDestroy);
|
2012-07-13 11:39:29 +00:00
|
|
|
|
2012-07-17 11:14:35 +00:00
|
|
|
if (priv->monitor) {
|
|
|
|
virLXCMonitorClose(priv->monitor);
|
2012-09-21 14:09:42 +00:00
|
|
|
virObjectUnref(priv->monitor);
|
2012-07-17 11:14:35 +00:00
|
|
|
priv->monitor = NULL;
|
|
|
|
}
|
2012-07-13 11:39:29 +00:00
|
|
|
|
2013-07-16 15:45:05 +00:00
|
|
|
virPidFileDelete(cfg->stateDir, vm->def->name);
|
2015-07-28 16:25:59 +00:00
|
|
|
lxcProcessRemoveDomainStatus(cfg, vm);
|
2012-07-13 11:39:29 +00:00
|
|
|
|
|
|
|
virDomainObjSetState(vm, VIR_DOMAIN_SHUTOFF, reason);
|
|
|
|
vm->pid = -1;
|
|
|
|
vm->def->id = -1;
|
|
|
|
|
2013-07-16 17:05:06 +00:00
|
|
|
if (virAtomicIntDecAndTest(&driver->nactive) && driver->inhibitCallback)
|
2012-10-31 19:03:55 +00:00
|
|
|
driver->inhibitCallback(false, driver->inhibitOpaque);
|
|
|
|
|
2012-11-23 14:46:18 +00:00
|
|
|
virLXCDomainReAttachHostDevices(driver, vm->def);
|
|
|
|
|
2013-05-21 08:03:33 +00:00
|
|
|
for (i = 0; i < vm->def->nnets; i++) {
|
2012-07-13 11:39:29 +00:00
|
|
|
virDomainNetDefPtr iface = vm->def->nets[i];
|
|
|
|
vport = virDomainNetGetActualVirtPortProfile(iface);
|
2013-01-22 22:09:26 +00:00
|
|
|
if (iface->ifname) {
|
|
|
|
if (vport &&
|
|
|
|
vport->virtPortType == VIR_NETDEV_VPORT_PROFILE_OPENVSWITCH)
|
|
|
|
ignore_value(virNetDevOpenvswitchRemovePort(
|
|
|
|
virDomainNetGetActualBridgeName(iface),
|
|
|
|
iface->ifname));
|
|
|
|
ignore_value(virNetDevVethDelete(iface->ifname));
|
|
|
|
}
|
2014-01-31 15:48:06 +00:00
|
|
|
networkReleaseActualDevice(vm->def, iface);
|
2012-07-13 11:39:29 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
virDomainConfVMNWFilterTeardown(vm);
|
|
|
|
|
2013-03-21 14:40:29 +00:00
|
|
|
if (priv->cgroup) {
|
|
|
|
virCgroupRemove(priv->cgroup);
|
|
|
|
virCgroupFree(&priv->cgroup);
|
2012-07-13 11:39:29 +00:00
|
|
|
}
|
|
|
|
|
2013-09-30 14:46:29 +00:00
|
|
|
/* Get machined to terminate the machine as it may not have cleaned it
|
|
|
|
* properly. See https://bugs.freedesktop.org/show_bug.cgi?id=68370 for
|
|
|
|
* the bug we are working around here.
|
|
|
|
*/
|
2016-02-01 15:50:54 +00:00
|
|
|
virCgroupTerminateMachine(priv->machineName);
|
2013-09-30 14:46:29 +00:00
|
|
|
|
2013-10-14 06:22:17 +00:00
|
|
|
/* The "release" hook cleans up additional resources */
|
2012-07-13 11:39:29 +00:00
|
|
|
if (virHookPresent(VIR_HOOK_DRIVER_LXC)) {
|
2016-02-03 21:40:35 +00:00
|
|
|
char *xml = virDomainDefFormat(vm->def, driver->caps, 0);
|
2012-07-13 11:39:29 +00:00
|
|
|
|
|
|
|
/* 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);
|
|
|
|
}
|
|
|
|
|
2016-09-08 13:16:58 +00:00
|
|
|
virDomainObjRemoveTransientDef(vm);
|
2013-07-16 15:45:05 +00:00
|
|
|
virObjectUnref(cfg);
|
2012-07-13 11:39:29 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2016-05-13 17:20:54 +00:00
|
|
|
int
|
|
|
|
virLXCProcessValidateInterface(virDomainNetDefPtr net)
|
|
|
|
{
|
|
|
|
if (net->script) {
|
|
|
|
virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
|
|
|
|
_("scripts are not supported on LXC network interfaces"));
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
char *
|
|
|
|
virLXCProcessSetupInterfaceTap(virDomainDefPtr vm,
|
|
|
|
virDomainNetDefPtr net,
|
|
|
|
const char *brname)
|
2012-07-13 11:39:29 +00:00
|
|
|
{
|
2012-11-27 12:55:02 +00:00
|
|
|
char *ret = NULL;
|
2012-07-13 11:39:29 +00:00
|
|
|
char *parentVeth;
|
|
|
|
char *containerVeth = NULL;
|
2013-10-08 17:12:17 +00:00
|
|
|
virNetDevVPortProfilePtr vport = virDomainNetGetActualVirtPortProfile(net);
|
2012-07-13 11:39:29 +00:00
|
|
|
|
|
|
|
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 (virNetDevSetMAC(containerVeth, &net->mac) < 0)
|
|
|
|
goto cleanup;
|
|
|
|
|
2016-05-13 17:20:54 +00:00
|
|
|
if (brname) {
|
|
|
|
if (vport && vport->virtPortType == VIR_NETDEV_VPORT_PROFILE_OPENVSWITCH) {
|
|
|
|
if (virNetDevOpenvswitchAddPort(brname, parentVeth, &net->mac, vm->uuid,
|
|
|
|
vport, virDomainNetGetActualVlan(net)) < 0)
|
|
|
|
goto cleanup;
|
|
|
|
} else {
|
|
|
|
if (virNetDevBridgeAddPort(brname, parentVeth) < 0)
|
|
|
|
goto cleanup;
|
|
|
|
}
|
2012-11-27 12:55:02 +00:00
|
|
|
}
|
2012-07-13 11:39:29 +00:00
|
|
|
|
|
|
|
if (virNetDevSetOnline(parentVeth, true) < 0)
|
|
|
|
goto cleanup;
|
|
|
|
|
2016-06-17 18:52:48 +00:00
|
|
|
if (virDomainNetGetActualType(net) == VIR_DOMAIN_NET_TYPE_ETHERNET) {
|
|
|
|
/* Set IP info for the host side, but only if the type is
|
|
|
|
* 'ethernet'.
|
|
|
|
*/
|
|
|
|
if (virNetDevIPInfoAddToDev(parentVeth, &net->hostIP) < 0)
|
|
|
|
goto cleanup;
|
|
|
|
}
|
|
|
|
|
2012-07-13 11:39:29 +00:00
|
|
|
if (net->filter &&
|
2014-11-07 10:37:37 +00:00
|
|
|
virDomainConfNWFilterInstantiate(vm->uuid, net) < 0)
|
2012-07-13 11:39:29 +00:00
|
|
|
goto cleanup;
|
|
|
|
|
2012-11-27 12:55:02 +00:00
|
|
|
ret = containerVeth;
|
2012-07-13 11:39:29 +00:00
|
|
|
|
2014-03-25 06:49:26 +00:00
|
|
|
cleanup:
|
2012-07-13 11:39:29 +00:00
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2012-11-27 12:55:02 +00:00
|
|
|
char *virLXCProcessSetupInterfaceDirect(virConnectPtr conn,
|
|
|
|
virDomainDefPtr def,
|
|
|
|
virDomainNetDefPtr net)
|
2012-07-13 11:39:29 +00:00
|
|
|
{
|
2012-11-27 12:55:02 +00:00
|
|
|
char *ret = NULL;
|
2012-07-13 11:39:29 +00:00
|
|
|
char *res_ifname = NULL;
|
2012-07-13 11:56:29 +00:00
|
|
|
virLXCDriverPtr driver = conn->privateData;
|
2012-07-13 11:39:29 +00:00
|
|
|
virNetDevBandwidthPtr bw;
|
|
|
|
virNetDevVPortProfilePtr prof;
|
2013-07-16 15:45:05 +00:00
|
|
|
virLXCDriverConfigPtr cfg = virLXCDriverGetConfig(driver);
|
2014-11-18 23:55:48 +00:00
|
|
|
const char *linkdev = virDomainNetGetActualDirectDev(net);
|
2014-09-16 20:50:53 +00:00
|
|
|
unsigned int macvlan_create_flags = VIR_NETDEV_MACVLAN_CREATE_IFUP;
|
2012-07-13 11:39:29 +00:00
|
|
|
|
|
|
|
/* 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) {
|
2012-07-13 12:59:51 +00:00
|
|
|
virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
|
|
|
|
_("Unable to set network bandwidth on direct interfaces"));
|
2012-11-27 12:55:02 +00:00
|
|
|
return NULL;
|
2012-07-13 11:39:29 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/* 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) {
|
2012-07-13 12:59:51 +00:00
|
|
|
virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
|
|
|
|
_("Unable to set port profile on direct interfaces"));
|
2012-11-27 12:55:02 +00:00
|
|
|
return NULL;
|
2012-07-13 11:39:29 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
if (virNetDevMacVLanCreateWithVPortProfile(
|
|
|
|
net->ifname, &net->mac,
|
2014-11-18 23:55:48 +00:00
|
|
|
linkdev,
|
2012-07-13 11:39:29 +00:00
|
|
|
virDomainNetGetActualDirectMode(net),
|
2016-05-04 17:18:16 +00:00
|
|
|
virDomainNetGetActualVlan(net),
|
2015-12-03 10:33:55 +00:00
|
|
|
def->uuid,
|
2014-11-18 23:55:48 +00:00
|
|
|
prof,
|
2012-07-13 11:39:29 +00:00
|
|
|
&res_ifname,
|
|
|
|
VIR_NETDEV_VPORT_PROFILE_OP_CREATE,
|
2014-09-16 20:50:53 +00:00
|
|
|
cfg->stateDir,
|
2015-12-04 10:31:17 +00:00
|
|
|
NULL, 0,
|
2014-09-16 20:50:53 +00:00
|
|
|
macvlan_create_flags) < 0)
|
2012-07-13 11:39:29 +00:00
|
|
|
goto cleanup;
|
|
|
|
|
2012-11-27 12:55:02 +00:00
|
|
|
ret = res_ifname;
|
2012-07-13 11:39:29 +00:00
|
|
|
|
2014-03-25 06:49:26 +00:00
|
|
|
cleanup:
|
2013-07-16 15:45:05 +00:00
|
|
|
virObjectUnref(cfg);
|
2012-07-13 11:39:29 +00:00
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2015-08-20 13:46:17 +00:00
|
|
|
static const char *nsInfoLocal[VIR_LXC_DOMAIN_NAMESPACE_LAST] = {
|
|
|
|
[VIR_LXC_DOMAIN_NAMESPACE_SHARENET] = "net",
|
|
|
|
[VIR_LXC_DOMAIN_NAMESPACE_SHAREIPC] = "ipc",
|
|
|
|
[VIR_LXC_DOMAIN_NAMESPACE_SHAREUTS] = "uts",
|
|
|
|
};
|
|
|
|
|
|
|
|
static int virLXCProcessSetupNamespaceName(virConnectPtr conn, int ns_type, const char *name)
|
|
|
|
{
|
|
|
|
virLXCDriverPtr driver = conn->privateData;
|
|
|
|
int fd = -1;
|
|
|
|
virDomainObjPtr vm;
|
|
|
|
virLXCDomainObjPrivatePtr priv;
|
|
|
|
char *path;
|
|
|
|
|
|
|
|
vm = virDomainObjListFindByName(driver->domains, name);
|
|
|
|
if (!vm) {
|
|
|
|
virReportError(VIR_ERR_NO_DOMAIN,
|
|
|
|
_("No domain with matching name '%s'"), name);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
priv = vm->privateData;
|
|
|
|
if (!priv->initpid) {
|
|
|
|
virReportError(VIR_ERR_OPERATION_INVALID, "%s",
|
|
|
|
_("Init pid is not yet available"));
|
|
|
|
goto cleanup;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (virAsprintf(&path, "/proc/%lld/ns/%s",
|
|
|
|
(long long int)priv->initpid,
|
|
|
|
nsInfoLocal[ns_type]) < 0)
|
|
|
|
goto cleanup;
|
|
|
|
|
|
|
|
if ((fd = open(path, O_RDONLY)) < 0) {
|
|
|
|
virReportSystemError(errno,
|
|
|
|
_("failed to open ns %s"),
|
|
|
|
virLXCDomainNamespaceTypeToString(ns_type));
|
|
|
|
goto cleanup;
|
|
|
|
}
|
|
|
|
|
|
|
|
cleanup:
|
|
|
|
VIR_FREE(path);
|
|
|
|
virObjectUnlock(vm);
|
|
|
|
virObjectUnref(vm);
|
|
|
|
return fd;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static int virLXCProcessSetupNamespacePID(int ns_type, const char *name)
|
|
|
|
{
|
|
|
|
int fd;
|
|
|
|
char *path;
|
|
|
|
|
|
|
|
if (virAsprintf(&path, "/proc/%s/ns/%s",
|
|
|
|
name,
|
|
|
|
nsInfoLocal[ns_type]) < 0)
|
|
|
|
return -1;
|
|
|
|
fd = open(path, O_RDONLY);
|
|
|
|
VIR_FREE(path);
|
|
|
|
if (fd < 0) {
|
|
|
|
virReportSystemError(errno,
|
|
|
|
_("failed to open ns %s"),
|
|
|
|
virLXCDomainNamespaceTypeToString(ns_type));
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
return fd;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static int virLXCProcessSetupNamespaceNet(int ns_type, const char *name)
|
|
|
|
{
|
|
|
|
char *path;
|
|
|
|
int fd;
|
|
|
|
if (ns_type != VIR_LXC_DOMAIN_NAMESPACE_SHARENET) {
|
|
|
|
virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
|
|
|
|
_("'netns' namespace source can only be "
|
|
|
|
"used with sharenet"));
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (virAsprintf(&path, "/var/run/netns/%s", name) < 0)
|
|
|
|
return -1;
|
|
|
|
fd = open(path, O_RDONLY);
|
|
|
|
VIR_FREE(path);
|
|
|
|
if (fd < 0) {
|
|
|
|
virReportSystemError(errno,
|
|
|
|
_("failed to open netns %s"), name);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
return fd;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
* virLXCProcessSetupNamespaces:
|
|
|
|
* @conn: pointer to connection
|
|
|
|
* @def: pointer to virtual machines namespaceData
|
|
|
|
* @nsFDs: out parameter to store the namespace FD
|
|
|
|
*
|
|
|
|
* Opens the specified namespace that needs to be shared and
|
|
|
|
* will moved into the container namespace later after clone has been called.
|
|
|
|
*
|
|
|
|
* Returns 0 on success or -1 in case of error
|
|
|
|
*/
|
|
|
|
static int virLXCProcessSetupNamespaces(virConnectPtr conn,
|
|
|
|
lxcDomainDefPtr lxcDef,
|
|
|
|
int *nsFDs)
|
|
|
|
{
|
|
|
|
size_t i;
|
|
|
|
|
|
|
|
for (i = 0; i < VIR_LXC_DOMAIN_NAMESPACE_LAST; i++)
|
|
|
|
nsFDs[i] = -1;
|
|
|
|
/*If there are no namespace to be opened just return success*/
|
|
|
|
if (lxcDef == NULL)
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
for (i = 0; i < VIR_LXC_DOMAIN_NAMESPACE_LAST; i++) {
|
|
|
|
switch (lxcDef->ns_source[i]) {
|
|
|
|
case VIR_LXC_DOMAIN_NAMESPACE_SOURCE_NONE:
|
|
|
|
continue;
|
|
|
|
case VIR_LXC_DOMAIN_NAMESPACE_SOURCE_NAME:
|
|
|
|
if ((nsFDs[i] = virLXCProcessSetupNamespaceName(conn, i, lxcDef->ns_val[i])) < 0)
|
|
|
|
return -1;
|
|
|
|
break;
|
|
|
|
case VIR_LXC_DOMAIN_NAMESPACE_SOURCE_PID:
|
|
|
|
if ((nsFDs[i] = virLXCProcessSetupNamespacePID(i, lxcDef->ns_val[i])) < 0)
|
|
|
|
return -1;
|
|
|
|
break;
|
|
|
|
case VIR_LXC_DOMAIN_NAMESPACE_SOURCE_NETNS:
|
|
|
|
if ((nsFDs[i] = virLXCProcessSetupNamespaceNet(i, lxcDef->ns_val[i])) < 0)
|
|
|
|
return -1;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
2012-07-13 11:39:29 +00:00
|
|
|
|
|
|
|
/**
|
2012-07-13 11:49:24 +00:00
|
|
|
* virLXCProcessSetupInterfaces:
|
2012-07-13 11:39:29 +00:00
|
|
|
* @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
|
|
|
|
*/
|
2012-07-13 11:49:24 +00:00
|
|
|
static int virLXCProcessSetupInterfaces(virConnectPtr conn,
|
|
|
|
virDomainDefPtr def,
|
2012-11-27 12:55:02 +00:00
|
|
|
size_t *nveths,
|
2012-07-13 11:49:24 +00:00
|
|
|
char ***veths)
|
2012-07-13 11:39:29 +00:00
|
|
|
{
|
|
|
|
int ret = -1;
|
|
|
|
size_t i;
|
2014-06-27 08:41:22 +00:00
|
|
|
size_t niface = 0;
|
2014-11-18 23:55:48 +00:00
|
|
|
virDomainNetDefPtr net;
|
|
|
|
virDomainNetType type;
|
2012-07-13 11:39:29 +00:00
|
|
|
|
2013-05-21 08:03:33 +00:00
|
|
|
for (i = 0; i < def->nnets; i++) {
|
2012-11-27 12:55:02 +00:00
|
|
|
char *veth = NULL;
|
2015-01-07 14:52:21 +00:00
|
|
|
virNetDevBandwidthPtr actualBandwidth;
|
2012-07-13 11:39:29 +00:00
|
|
|
/* 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.
|
|
|
|
*/
|
2014-11-18 23:55:48 +00:00
|
|
|
net = def->nets[i];
|
2016-05-13 17:20:54 +00:00
|
|
|
|
|
|
|
if (virLXCProcessValidateInterface(net) < 0)
|
|
|
|
return -1;
|
|
|
|
|
2014-11-18 23:55:48 +00:00
|
|
|
if (networkAllocateActualDevice(def, net) < 0)
|
2012-07-13 11:39:29 +00:00
|
|
|
goto cleanup;
|
|
|
|
|
2013-07-04 10:11:37 +00:00
|
|
|
if (VIR_EXPAND_N(*veths, *nveths, 1) < 0)
|
2012-11-27 12:55:02 +00:00
|
|
|
goto cleanup;
|
|
|
|
|
2014-11-18 23:55:48 +00:00
|
|
|
type = virDomainNetGetActualType(net);
|
|
|
|
switch (type) {
|
2014-11-21 20:08:50 +00:00
|
|
|
case VIR_DOMAIN_NET_TYPE_NETWORK:
|
2012-07-13 11:39:29 +00:00
|
|
|
case VIR_DOMAIN_NET_TYPE_BRIDGE: {
|
2014-11-18 23:55:48 +00:00
|
|
|
const char *brname = virDomainNetGetActualBridgeName(net);
|
2012-07-13 11:39:29 +00:00
|
|
|
if (!brname) {
|
2012-07-13 12:59:51 +00:00
|
|
|
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
|
|
|
|
_("No bridge name specified"));
|
2012-07-13 11:39:29 +00:00
|
|
|
goto cleanup;
|
|
|
|
}
|
2016-05-13 17:20:54 +00:00
|
|
|
if (!(veth = virLXCProcessSetupInterfaceTap(def, net, brname)))
|
2012-07-13 11:39:29 +00:00
|
|
|
goto cleanup;
|
|
|
|
} break;
|
2016-05-13 17:20:54 +00:00
|
|
|
case VIR_DOMAIN_NET_TYPE_ETHERNET:
|
|
|
|
if (!(veth = virLXCProcessSetupInterfaceTap(def, net, NULL)))
|
|
|
|
goto cleanup;
|
|
|
|
break;
|
2012-07-13 11:39:29 +00:00
|
|
|
case VIR_DOMAIN_NET_TYPE_DIRECT:
|
2016-05-13 17:20:54 +00:00
|
|
|
if (!(veth = virLXCProcessSetupInterfaceDirect(conn, def, net)))
|
2012-07-13 11:39:29 +00:00
|
|
|
goto cleanup;
|
|
|
|
break;
|
|
|
|
|
2014-11-18 23:55:48 +00:00
|
|
|
case VIR_DOMAIN_NET_TYPE_USER:
|
2014-07-11 17:47:31 +00:00
|
|
|
case VIR_DOMAIN_NET_TYPE_VHOSTUSER:
|
2012-07-13 11:39:29 +00:00
|
|
|
case VIR_DOMAIN_NET_TYPE_SERVER:
|
|
|
|
case VIR_DOMAIN_NET_TYPE_CLIENT:
|
|
|
|
case VIR_DOMAIN_NET_TYPE_MCAST:
|
2015-08-29 20:19:10 +00:00
|
|
|
case VIR_DOMAIN_NET_TYPE_UDP:
|
2012-07-13 11:39:29 +00:00
|
|
|
case VIR_DOMAIN_NET_TYPE_INTERNAL:
|
|
|
|
case VIR_DOMAIN_NET_TYPE_LAST:
|
2014-11-18 23:55:48 +00:00
|
|
|
case VIR_DOMAIN_NET_TYPE_HOSTDEV:
|
2012-07-13 12:59:51 +00:00
|
|
|
virReportError(VIR_ERR_INTERNAL_ERROR,
|
|
|
|
_("Unsupported network type %s"),
|
2014-11-18 23:55:48 +00:00
|
|
|
virDomainNetTypeToString(type));
|
2012-07-13 11:39:29 +00:00
|
|
|
goto cleanup;
|
2014-11-18 23:55:48 +00:00
|
|
|
|
2012-07-13 11:39:29 +00:00
|
|
|
}
|
2012-11-27 12:55:02 +00:00
|
|
|
|
2015-01-07 14:52:21 +00:00
|
|
|
/* Set bandwidth or warn if requested and not supported. */
|
|
|
|
actualBandwidth = virDomainNetGetActualBandwidth(net);
|
|
|
|
if (actualBandwidth) {
|
|
|
|
if (virNetDevSupportBandwidth(type)) {
|
|
|
|
if (virNetDevBandwidthSet(net->ifname, actualBandwidth, false) < 0)
|
|
|
|
goto cleanup;
|
|
|
|
} else {
|
|
|
|
VIR_WARN("setting bandwidth on interfaces of "
|
|
|
|
"type '%s' is not implemented yet",
|
|
|
|
virDomainNetTypeToString(type));
|
|
|
|
}
|
|
|
|
}
|
2014-11-18 23:55:48 +00:00
|
|
|
|
2012-11-27 12:55:02 +00:00
|
|
|
(*veths)[(*nveths)-1] = veth;
|
2014-06-27 08:41:22 +00:00
|
|
|
|
2014-12-19 09:08:38 +00:00
|
|
|
if (VIR_STRDUP(def->nets[i]->ifname_guest_actual, veth) < 0)
|
|
|
|
goto cleanup;
|
|
|
|
|
2014-06-27 08:41:22 +00:00
|
|
|
/* Make sure all net definitions will have a name in the container */
|
2014-11-18 23:55:48 +00:00
|
|
|
if (!net->ifname_guest) {
|
|
|
|
if (virAsprintf(&net->ifname_guest, "eth%zu", niface) < 0)
|
2014-06-27 08:41:22 +00:00
|
|
|
return -1;
|
|
|
|
niface++;
|
|
|
|
}
|
2012-07-13 11:39:29 +00:00
|
|
|
}
|
|
|
|
|
2012-11-23 16:38:01 +00:00
|
|
|
ret = 0;
|
2012-07-13 11:39:29 +00:00
|
|
|
|
2014-03-25 06:49:26 +00:00
|
|
|
cleanup:
|
2012-11-23 16:38:01 +00:00
|
|
|
if (ret < 0) {
|
2013-05-21 08:03:33 +00:00
|
|
|
for (i = 0; i < def->nnets; i++) {
|
2012-07-13 11:39:29 +00:00
|
|
|
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));
|
2014-01-31 15:48:06 +00:00
|
|
|
networkReleaseActualDevice(def, iface);
|
2012-07-13 11:39:29 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2014-06-27 08:41:22 +00:00
|
|
|
static void
|
|
|
|
virLXCProcessCleanInterfaces(virDomainDefPtr def)
|
|
|
|
{
|
|
|
|
size_t i;
|
|
|
|
|
|
|
|
for (i = 0; i < def->nnets; i++) {
|
|
|
|
VIR_FREE(def->nets[i]->ifname_guest_actual);
|
|
|
|
VIR_DEBUG("Cleared net names: %s", def->nets[i]->ifname_guest);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2012-07-13 11:39:29 +00:00
|
|
|
|
2012-07-17 10:55:38 +00:00
|
|
|
extern virLXCDriverPtr lxc_driver;
|
Fix deadlock in handling EOF in LXC monitor
Depending on the scenario in which LXC containers exit, it is
possible for the EOF callback of the LXC monitor to deadlock
the driver.
#0 0x00000038a0a0de4d in __lll_lock_wait () from /lib64/libpthread.so.0
#1 0x00000038a0a09ca6 in _L_lock_840 () from /lib64/libpthread.so.0
#2 0x00000038a0a09ba8 in pthread_mutex_lock () from /lib64/libpthread.so.0
#3 0x00007f4bd9579d55 in virMutexLock (m=<optimized out>) at util/threads-pthread.c:85
#4 0x00007f4bcacc7597 in lxcDriverLock (driver=0x7f4bc40c8290) at lxc/lxc_conf.h:81
#5 virLXCProcessMonitorEOFNotify (mon=<optimized out>, vm=0x7f4bb4000b00) at lxc/lxc_process.c:581
#6 0x00007f4bd9645c91 in virNetClientCloseLocked (client=client@entry=0x7f4bb4009e60)
at rpc/virnetclient.c:554
#7 0x00007f4bd96460f8 in virNetClientIOEventLoopPassTheBuck (thiscall=0x0, client=0x7f4bb4009e60)
at rpc/virnetclient.c:1306
#8 virNetClientIOEventLoopPassTheBuck (client=0x7f4bb4009e60, thiscall=0x0)
at rpc/virnetclient.c:1287
#9 0x00007f4bd96467a2 in virNetClientCloseInternal (reason=3, client=0x7f4bb4009e60)
at rpc/virnetclient.c:589
#10 virNetClientCloseInternal (client=0x7f4bb4009e60, reason=3) at rpc/virnetclient.c:561
#11 0x00007f4bcacc4a82 in virLXCMonitorClose (mon=0x7f4bb4000a00) at lxc/lxc_monitor.c:201
#12 0x00007f4bcacc55ac in virLXCProcessCleanup (reason=<optimized out>, vm=0x7f4bb4000b00,
driver=0x7f4bc40c8290) at lxc/lxc_process.c:240
#13 virLXCProcessStop (driver=0x7f4bc40c8290, vm=vm@entry=0x7f4bb4000b00,
reason=reason@entry=VIR_DOMAIN_SHUTOFF_DESTROYED) at lxc/lxc_process.c:735
#14 0x00007f4bcacc5bd2 in virLXCProcessAutoDestroyDom (payload=<optimized out>,
name=0x7f4bb4003c80, opaque=0x7fff41af2df0) at lxc/lxc_process.c:94
#15 0x00007f4bd9586649 in virHashForEach (table=0x7f4bc409b270,
iter=iter@entry=0x7f4bcacc5ab0 <virLXCProcessAutoDestroyDom>, data=data@entry=0x7fff41af2df0)
at util/virhash.c:514
#16 0x00007f4bcacc52d7 in virLXCProcessAutoDestroyRun (driver=driver@entry=0x7f4bc40c8290,
conn=conn@entry=0x7f4bb8000ab0) at lxc/lxc_process.c:120
#17 0x00007f4bcacca628 in lxcClose (conn=0x7f4bb8000ab0) at lxc/lxc_driver.c:128
#18 0x00007f4bd95e67ab in virReleaseConnect (conn=conn@entry=0x7f4bb8000ab0) at datatypes.c:114
When the driver calls virLXCMonitorClose, there is really no
need for the EOF callback to be invoked in this case, since
the caller can easily handle events itself. In changing this,
the monitor needs to take a deep copy of the callback list,
not merely a reference.
Also adds debug statements in various places to aid
troubleshooting
Signed-off-by: Daniel P. Berrange <berrange@redhat.com>
2012-09-26 14:08:20 +00:00
|
|
|
static void virLXCProcessMonitorEOFNotify(virLXCMonitorPtr mon,
|
2012-07-17 11:14:35 +00:00
|
|
|
virDomainObjPtr vm)
|
2012-07-17 10:55:38 +00:00
|
|
|
{
|
|
|
|
virLXCDriverPtr driver = lxc_driver;
|
2013-11-22 14:38:05 +00:00
|
|
|
virObjectEventPtr event = NULL;
|
2012-07-17 10:55:38 +00:00
|
|
|
virLXCDomainObjPrivatePtr priv;
|
|
|
|
|
Fix deadlock in handling EOF in LXC monitor
Depending on the scenario in which LXC containers exit, it is
possible for the EOF callback of the LXC monitor to deadlock
the driver.
#0 0x00000038a0a0de4d in __lll_lock_wait () from /lib64/libpthread.so.0
#1 0x00000038a0a09ca6 in _L_lock_840 () from /lib64/libpthread.so.0
#2 0x00000038a0a09ba8 in pthread_mutex_lock () from /lib64/libpthread.so.0
#3 0x00007f4bd9579d55 in virMutexLock (m=<optimized out>) at util/threads-pthread.c:85
#4 0x00007f4bcacc7597 in lxcDriverLock (driver=0x7f4bc40c8290) at lxc/lxc_conf.h:81
#5 virLXCProcessMonitorEOFNotify (mon=<optimized out>, vm=0x7f4bb4000b00) at lxc/lxc_process.c:581
#6 0x00007f4bd9645c91 in virNetClientCloseLocked (client=client@entry=0x7f4bb4009e60)
at rpc/virnetclient.c:554
#7 0x00007f4bd96460f8 in virNetClientIOEventLoopPassTheBuck (thiscall=0x0, client=0x7f4bb4009e60)
at rpc/virnetclient.c:1306
#8 virNetClientIOEventLoopPassTheBuck (client=0x7f4bb4009e60, thiscall=0x0)
at rpc/virnetclient.c:1287
#9 0x00007f4bd96467a2 in virNetClientCloseInternal (reason=3, client=0x7f4bb4009e60)
at rpc/virnetclient.c:589
#10 virNetClientCloseInternal (client=0x7f4bb4009e60, reason=3) at rpc/virnetclient.c:561
#11 0x00007f4bcacc4a82 in virLXCMonitorClose (mon=0x7f4bb4000a00) at lxc/lxc_monitor.c:201
#12 0x00007f4bcacc55ac in virLXCProcessCleanup (reason=<optimized out>, vm=0x7f4bb4000b00,
driver=0x7f4bc40c8290) at lxc/lxc_process.c:240
#13 virLXCProcessStop (driver=0x7f4bc40c8290, vm=vm@entry=0x7f4bb4000b00,
reason=reason@entry=VIR_DOMAIN_SHUTOFF_DESTROYED) at lxc/lxc_process.c:735
#14 0x00007f4bcacc5bd2 in virLXCProcessAutoDestroyDom (payload=<optimized out>,
name=0x7f4bb4003c80, opaque=0x7fff41af2df0) at lxc/lxc_process.c:94
#15 0x00007f4bd9586649 in virHashForEach (table=0x7f4bc409b270,
iter=iter@entry=0x7f4bcacc5ab0 <virLXCProcessAutoDestroyDom>, data=data@entry=0x7fff41af2df0)
at util/virhash.c:514
#16 0x00007f4bcacc52d7 in virLXCProcessAutoDestroyRun (driver=driver@entry=0x7f4bc40c8290,
conn=conn@entry=0x7f4bb8000ab0) at lxc/lxc_process.c:120
#17 0x00007f4bcacca628 in lxcClose (conn=0x7f4bb8000ab0) at lxc/lxc_driver.c:128
#18 0x00007f4bd95e67ab in virReleaseConnect (conn=conn@entry=0x7f4bb8000ab0) at datatypes.c:114
When the driver calls virLXCMonitorClose, there is really no
need for the EOF callback to be invoked in this case, since
the caller can easily handle events itself. In changing this,
the monitor needs to take a deep copy of the callback list,
not merely a reference.
Also adds debug statements in various places to aid
troubleshooting
Signed-off-by: Daniel P. Berrange <berrange@redhat.com>
2012-09-26 14:08:20 +00:00
|
|
|
VIR_DEBUG("mon=%p vm=%p", mon, vm);
|
|
|
|
|
2013-01-09 21:00:32 +00:00
|
|
|
virObjectLock(vm);
|
2012-07-17 10:55:38 +00:00
|
|
|
|
|
|
|
priv = vm->privateData;
|
|
|
|
virLXCProcessStop(driver, vm, VIR_DOMAIN_SHUTOFF_SHUTDOWN);
|
2012-07-23 11:25:37 +00:00
|
|
|
if (!priv->wantReboot) {
|
|
|
|
virLXCProcessStop(driver, vm, VIR_DOMAIN_SHUTOFF_SHUTDOWN);
|
|
|
|
if (!priv->doneStopEvent) {
|
2013-11-21 17:03:26 +00:00
|
|
|
event = virDomainEventLifecycleNewFromObj(vm,
|
2012-07-23 11:25:37 +00:00
|
|
|
VIR_DOMAIN_EVENT_STOPPED,
|
|
|
|
priv->stopReason);
|
|
|
|
virDomainAuditStop(vm, "shutdown");
|
|
|
|
} else {
|
|
|
|
VIR_DEBUG("Stop event has already been sent");
|
|
|
|
}
|
|
|
|
if (!vm->persistent) {
|
2013-01-11 16:04:47 +00:00
|
|
|
virDomainObjListRemove(driver->domains, vm);
|
2012-07-23 11:25:37 +00:00
|
|
|
vm = NULL;
|
|
|
|
}
|
2012-07-17 10:55:38 +00:00
|
|
|
} else {
|
2012-07-23 11:25:37 +00:00
|
|
|
int ret = virLXCProcessReboot(driver, vm);
|
|
|
|
virDomainAuditStop(vm, "reboot");
|
|
|
|
virDomainAuditStart(vm, "reboot", ret == 0);
|
|
|
|
if (ret == 0) {
|
|
|
|
event = virDomainEventRebootNewFromObj(vm);
|
|
|
|
} else {
|
2013-11-21 17:03:26 +00:00
|
|
|
event = virDomainEventLifecycleNewFromObj(vm,
|
2012-07-23 11:25:37 +00:00
|
|
|
VIR_DOMAIN_EVENT_STOPPED,
|
|
|
|
priv->stopReason);
|
|
|
|
if (!vm->persistent) {
|
2013-01-11 16:04:47 +00:00
|
|
|
virDomainObjListRemove(driver->domains, vm);
|
2012-07-23 11:25:37 +00:00
|
|
|
vm = NULL;
|
|
|
|
}
|
|
|
|
}
|
2012-07-17 10:55:38 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
if (vm)
|
2013-01-09 21:00:32 +00:00
|
|
|
virObjectUnlock(vm);
|
2014-11-13 14:27:11 +00:00
|
|
|
if (event)
|
2013-11-21 10:43:10 +00:00
|
|
|
virObjectEventStateQueue(driver->domainEventState, event);
|
2012-07-17 10:55:38 +00:00
|
|
|
}
|
|
|
|
|
2012-07-17 14:54:08 +00:00
|
|
|
static void virLXCProcessMonitorExitNotify(virLXCMonitorPtr mon ATTRIBUTE_UNUSED,
|
2013-03-13 19:19:22 +00:00
|
|
|
virLXCMonitorExitStatus status,
|
2012-07-17 14:54:08 +00:00
|
|
|
virDomainObjPtr vm)
|
|
|
|
{
|
|
|
|
virLXCDomainObjPrivatePtr priv = vm->privateData;
|
|
|
|
|
2013-03-22 12:10:39 +00:00
|
|
|
virObjectLock(vm);
|
|
|
|
|
2012-07-17 14:54:08 +00:00
|
|
|
switch (status) {
|
2013-03-13 19:19:22 +00:00
|
|
|
case VIR_LXC_MONITOR_EXIT_STATUS_SHUTDOWN:
|
2012-07-17 14:54:08 +00:00
|
|
|
priv->stopReason = VIR_DOMAIN_EVENT_STOPPED_SHUTDOWN;
|
|
|
|
break;
|
2013-03-13 19:19:22 +00:00
|
|
|
case VIR_LXC_MONITOR_EXIT_STATUS_ERROR:
|
2012-07-17 14:54:08 +00:00
|
|
|
priv->stopReason = VIR_DOMAIN_EVENT_STOPPED_FAILED;
|
|
|
|
break;
|
2013-03-13 19:19:22 +00:00
|
|
|
case VIR_LXC_MONITOR_EXIT_STATUS_REBOOT:
|
2012-07-23 11:25:37 +00:00
|
|
|
priv->stopReason = VIR_DOMAIN_EVENT_STOPPED_SHUTDOWN;
|
|
|
|
priv->wantReboot = true;
|
|
|
|
break;
|
2012-07-17 14:54:08 +00:00
|
|
|
default:
|
|
|
|
priv->stopReason = VIR_DOMAIN_EVENT_STOPPED_FAILED;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
VIR_DEBUG("Domain shutoff reason %d (from status %d)",
|
|
|
|
priv->stopReason, status);
|
2013-03-22 12:10:39 +00:00
|
|
|
|
|
|
|
virObjectUnlock(vm);
|
2012-07-17 14:54:08 +00:00
|
|
|
}
|
2012-07-17 10:55:38 +00:00
|
|
|
|
2013-03-06 14:56:49 +00:00
|
|
|
static int
|
|
|
|
virLXCProcessGetNsInode(pid_t pid,
|
|
|
|
const char *nsname,
|
|
|
|
ino_t *inode)
|
|
|
|
{
|
|
|
|
char *path = NULL;
|
|
|
|
struct stat sb;
|
|
|
|
int ret = -1;
|
|
|
|
|
2016-10-06 14:54:41 +00:00
|
|
|
if (virAsprintf(&path, "/proc/%lld/ns/%s",
|
|
|
|
(long long) pid, nsname) < 0)
|
2013-03-06 14:56:49 +00:00
|
|
|
goto cleanup;
|
|
|
|
|
|
|
|
if (stat(path, &sb) < 0) {
|
|
|
|
virReportSystemError(errno,
|
|
|
|
_("Unable to stat %s"), path);
|
|
|
|
goto cleanup;
|
|
|
|
}
|
|
|
|
|
|
|
|
*inode = sb.st_ino;
|
|
|
|
ret = 0;
|
|
|
|
|
2014-03-25 06:49:26 +00:00
|
|
|
cleanup:
|
2013-03-06 14:56:49 +00:00
|
|
|
VIR_FREE(path);
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2012-11-22 17:15:54 +00:00
|
|
|
/* XXX a little evil */
|
|
|
|
extern virLXCDriverPtr lxc_driver;
|
2012-11-20 17:49:25 +00:00
|
|
|
static void virLXCProcessMonitorInitNotify(virLXCMonitorPtr mon ATTRIBUTE_UNUSED,
|
|
|
|
pid_t initpid,
|
|
|
|
virDomainObjPtr vm)
|
|
|
|
{
|
2013-03-22 12:10:39 +00:00
|
|
|
virLXCDriverPtr driver = lxc_driver;
|
|
|
|
virLXCDomainObjPrivatePtr priv;
|
2013-07-17 07:20:26 +00:00
|
|
|
virLXCDriverConfigPtr cfg = virLXCDriverGetConfig(driver);
|
2016-03-04 14:21:36 +00:00
|
|
|
ino_t inode = 0;
|
2013-03-06 14:56:49 +00:00
|
|
|
|
2013-03-22 12:10:39 +00:00
|
|
|
virObjectLock(vm);
|
|
|
|
|
|
|
|
priv = vm->privateData;
|
2012-11-22 17:15:54 +00:00
|
|
|
priv->initpid = initpid;
|
2013-03-06 14:56:49 +00:00
|
|
|
|
|
|
|
if (virLXCProcessGetNsInode(initpid, "pid", &inode) < 0) {
|
2016-10-06 14:54:41 +00:00
|
|
|
VIR_WARN("Cannot obtain pid NS inode for %lld: %s",
|
|
|
|
(long long) initpid,
|
2016-05-19 19:10:19 +00:00
|
|
|
virGetLastErrorMessage());
|
2013-03-06 14:56:49 +00:00
|
|
|
virResetLastError();
|
|
|
|
}
|
|
|
|
virDomainAuditInit(vm, initpid, inode);
|
2012-11-22 17:15:54 +00:00
|
|
|
|
2016-02-04 12:32:45 +00:00
|
|
|
if (virDomainSaveStatus(lxc_driver->xmlopt, cfg->stateDir, vm, lxc_driver->caps) < 0)
|
2012-11-22 17:15:54 +00:00
|
|
|
VIR_WARN("Cannot update XML with PID for LXC %s", vm->def->name);
|
2013-03-22 12:10:39 +00:00
|
|
|
|
|
|
|
virObjectUnlock(vm);
|
2013-07-16 15:45:05 +00:00
|
|
|
virObjectUnref(cfg);
|
2012-11-20 17:49:25 +00:00
|
|
|
}
|
|
|
|
|
2012-07-17 11:14:35 +00:00
|
|
|
static virLXCMonitorCallbacks monitorCallbacks = {
|
|
|
|
.eofNotify = virLXCProcessMonitorEOFNotify,
|
2012-07-17 14:54:08 +00:00
|
|
|
.exitNotify = virLXCProcessMonitorExitNotify,
|
2012-11-20 17:49:25 +00:00
|
|
|
.initNotify = virLXCProcessMonitorInitNotify,
|
2012-07-17 11:14:35 +00:00
|
|
|
};
|
2012-07-13 11:39:29 +00:00
|
|
|
|
2012-07-17 11:14:35 +00:00
|
|
|
|
|
|
|
static virLXCMonitorPtr virLXCProcessConnectMonitor(virLXCDriverPtr driver,
|
|
|
|
virDomainObjPtr vm)
|
|
|
|
{
|
|
|
|
virLXCMonitorPtr monitor = NULL;
|
2013-07-16 15:45:05 +00:00
|
|
|
virLXCDriverConfigPtr cfg = virLXCDriverGetConfig(driver);
|
2012-07-13 11:39:29 +00:00
|
|
|
|
2012-07-17 10:55:38 +00:00
|
|
|
if (virSecurityManagerSetSocketLabel(driver->securityManager, vm->def) < 0)
|
|
|
|
goto cleanup;
|
2012-07-13 11:39:29 +00:00
|
|
|
|
2012-07-17 11:14:35 +00:00
|
|
|
/* Hold an extra reference because we can't allow 'vm' to be
|
|
|
|
* deleted while the monitor is active */
|
2012-07-11 13:35:46 +00:00
|
|
|
virObjectRef(vm);
|
2012-07-17 11:14:35 +00:00
|
|
|
|
2013-07-16 15:45:05 +00:00
|
|
|
monitor = virLXCMonitorNew(vm, cfg->stateDir, &monitorCallbacks);
|
2012-07-17 11:14:35 +00:00
|
|
|
|
|
|
|
if (monitor == NULL)
|
2012-07-11 13:35:46 +00:00
|
|
|
virObjectUnref(vm);
|
2012-07-13 11:39:29 +00:00
|
|
|
|
|
|
|
if (virSecurityManagerClearSocketLabel(driver->securityManager, vm->def) < 0) {
|
2012-07-17 11:14:35 +00:00
|
|
|
if (monitor) {
|
2012-09-21 14:09:42 +00:00
|
|
|
virObjectUnref(monitor);
|
2012-07-17 11:14:35 +00:00
|
|
|
monitor = NULL;
|
|
|
|
}
|
2012-07-17 10:55:38 +00:00
|
|
|
goto cleanup;
|
2012-07-13 11:39:29 +00:00
|
|
|
}
|
|
|
|
|
2014-03-25 06:49:26 +00:00
|
|
|
cleanup:
|
2013-07-16 15:45:05 +00:00
|
|
|
virObjectUnref(cfg);
|
2012-07-17 10:55:38 +00:00
|
|
|
return monitor;
|
2012-07-13 11:39:29 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2012-07-13 11:56:29 +00:00
|
|
|
int virLXCProcessStop(virLXCDriverPtr driver,
|
2012-07-13 11:49:24 +00:00
|
|
|
virDomainObjPtr vm,
|
|
|
|
virDomainShutoffReason reason)
|
2012-07-13 11:39:29 +00:00
|
|
|
{
|
|
|
|
int rc;
|
2013-03-21 14:40:29 +00:00
|
|
|
virLXCDomainObjPrivatePtr priv;
|
2012-07-13 11:39:29 +00:00
|
|
|
|
2012-07-17 14:54:08 +00:00
|
|
|
VIR_DEBUG("Stopping VM name=%s pid=%d reason=%d",
|
|
|
|
vm->def->name, (int)vm->pid, (int)reason);
|
2013-03-04 20:47:37 +00:00
|
|
|
if (!virDomainObjIsActive(vm)) {
|
|
|
|
VIR_DEBUG("VM '%s' not active", vm->def->name);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2013-03-21 14:40:29 +00:00
|
|
|
priv = vm->privateData;
|
|
|
|
|
2012-07-13 11:39:29 +00:00
|
|
|
if (vm->pid <= 0) {
|
2012-07-13 12:59:51 +00:00
|
|
|
virReportError(VIR_ERR_INTERNAL_ERROR,
|
|
|
|
_("Invalid PID %d for container"), vm->pid);
|
2012-07-13 11:39:29 +00:00
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
virSecurityManagerRestoreAllLabel(driver->securityManager,
|
2017-05-29 12:27:51 +00:00
|
|
|
vm->def, false, false);
|
2012-07-13 11:39:29 +00:00
|
|
|
virSecurityManagerReleaseLabel(driver->securityManager, vm->def);
|
|
|
|
/* Clear out dynamically assigned labels */
|
2012-08-15 22:10:35 +00:00
|
|
|
if (vm->def->nseclabels &&
|
|
|
|
vm->def->seclabels[0]->type == VIR_DOMAIN_SECLABEL_DYNAMIC) {
|
|
|
|
VIR_FREE(vm->def->seclabels[0]->model);
|
|
|
|
VIR_FREE(vm->def->seclabels[0]->label);
|
|
|
|
VIR_FREE(vm->def->seclabels[0]->imagelabel);
|
2012-07-13 11:39:29 +00:00
|
|
|
}
|
|
|
|
|
2014-02-14 15:42:48 +00:00
|
|
|
/* If the LXC domain is suspended we send all processes a SIGKILL
|
|
|
|
* and thaw them. Upon wakeup the process sees the pending signal
|
|
|
|
* and dies immediately. It is guaranteed that priv->cgroup != NULL
|
|
|
|
* here because the domain has aleady been suspended using the
|
|
|
|
* freezer cgroup.
|
|
|
|
*/
|
|
|
|
if (reason == VIR_DOMAIN_SHUTOFF_DESTROYED &&
|
|
|
|
virDomainObjGetState(vm, NULL) == VIR_DOMAIN_PAUSED) {
|
|
|
|
if (virCgroupKillRecursive(priv->cgroup, SIGKILL) <= 0) {
|
|
|
|
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
|
|
|
|
_("Unable to kill all processes"));
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (virCgroupSetFreezerState(priv->cgroup, "THAWED") < 0) {
|
|
|
|
virReportError(VIR_ERR_OPERATION_FAILED, "%s",
|
|
|
|
_("Unable to thaw all processes"));
|
|
|
|
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
goto cleanup;
|
|
|
|
}
|
|
|
|
|
2013-03-21 14:40:29 +00:00
|
|
|
if (priv->cgroup) {
|
|
|
|
rc = virCgroupKillPainfully(priv->cgroup);
|
2013-07-19 14:43:04 +00:00
|
|
|
if (rc < 0)
|
|
|
|
return -1;
|
|
|
|
if (rc > 0) {
|
2012-07-13 12:59:51 +00:00
|
|
|
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
|
2013-07-19 14:43:04 +00:00
|
|
|
_("Some processes refused to die"));
|
|
|
|
return -1;
|
2012-07-13 11:39:29 +00:00
|
|
|
}
|
|
|
|
} else {
|
2013-07-22 15:36:06 +00:00
|
|
|
/* If cgroup doesn't exist, just try cleaning up the
|
|
|
|
* libvirt_lxc process */
|
2013-12-13 08:30:36 +00:00
|
|
|
if (virProcessKillPainfully(vm->pid, true) < 0) {
|
|
|
|
virReportError(VIR_ERR_INTERNAL_ERROR,
|
|
|
|
_("Processes %d refused to die"), (int)vm->pid);
|
|
|
|
return -1;
|
|
|
|
}
|
2012-07-13 11:39:29 +00:00
|
|
|
}
|
|
|
|
|
2014-03-25 06:49:26 +00:00
|
|
|
cleanup:
|
2012-07-13 11:49:24 +00:00
|
|
|
virLXCProcessCleanup(driver, vm, reason);
|
2012-07-13 11:39:29 +00:00
|
|
|
|
2013-07-19 14:43:04 +00:00
|
|
|
return 0;
|
2012-07-13 11:39:29 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static virCommandPtr
|
2012-07-13 11:56:29 +00:00
|
|
|
virLXCProcessBuildControllerCmd(virLXCDriverPtr driver,
|
2012-07-13 11:49:24 +00:00
|
|
|
virDomainObjPtr vm,
|
|
|
|
int nveths,
|
|
|
|
char **veths,
|
|
|
|
int *ttyFDs,
|
|
|
|
size_t nttyFDs,
|
2015-08-20 13:46:17 +00:00
|
|
|
int *nsInheritFDs,
|
2013-07-09 17:15:45 +00:00
|
|
|
int *files,
|
|
|
|
size_t nfiles,
|
2015-01-16 15:03:16 +00:00
|
|
|
int handshakefd,
|
lxc: Don't pass a local variable address randomly
So, recently I was testing the LXC driver. You know, startup some
domains. But to my surprise, I was not able to start a single one:
virsh # start --console test
error: Reconnected to the hypervisor
error: Failed to start domain test
error: internal error: guest failed to start: unexpected exit status 125
So I've start digging. It turns out, that in virExec(), when I printed
out the @cmd, I got strange values: *(cmd->outfdptr) was certainly not
valid FD number: it has random value of several millions. This
obviously made prepareStdFd(childout, STDOUT_FILENO) fail (line 611).
But outfdptr is set in virCommandSetOutputFD(). The only place within
LXC driver where the function is called is in
virLXCProcessBuildControllerCmd(). If you take a closer look at the
function it looks like this:
static virCommandPtr
virLXCProcessBuildControllerCmd(virLXCDriverPtr driver,
..
int logfd,
const char *pidfile)
{
...
virCommandSetOutputFD(cmd, &logfd);
virCommandSetErrorFD(cmd, &logfd);
...
}
Yes, you guessed it. @logfd is passed into the function by value.
However, in the function we try to get its address (an address of a
local variable) which is no longer valid once function is finished and
stack is cleaned. Therefore when cmd->outfdptr is evaluated at any
point after this function, we may get a random number, depending on
what's currently on the stack. Of course, this may work sometimes too
- it depends on the compiler how it arranges the code, when the stack
is wiped out.
In order to fix this, lets pass a pointer to @logfd instead of
figuring out (wrong) its value in a function.
The bug was introduced in e1de5521.
Signed-off-by: Michal Privoznik <mprivozn@redhat.com>
2015-07-01 15:21:28 +00:00
|
|
|
int * const logfd,
|
2015-01-16 15:03:16 +00:00
|
|
|
const char *pidfile)
|
2012-07-13 11:39:29 +00:00
|
|
|
{
|
|
|
|
size_t i;
|
|
|
|
char *filterstr;
|
|
|
|
char *outputstr;
|
|
|
|
virCommandPtr cmd;
|
2013-07-16 15:45:05 +00:00
|
|
|
virLXCDriverConfigPtr cfg = virLXCDriverGetConfig(driver);
|
2012-07-13 11:39:29 +00:00
|
|
|
|
|
|
|
cmd = virCommandNew(vm->def->emulator);
|
|
|
|
|
|
|
|
/* The controller may call ip command, so we have to retain PATH. */
|
2013-10-09 10:03:02 +00:00
|
|
|
virCommandAddEnvPassBlockSUID(cmd, "PATH", "/bin:/usr/bin");
|
2012-07-13 11:39:29 +00:00
|
|
|
|
|
|
|
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);
|
|
|
|
}
|
|
|
|
|
2013-07-16 15:45:05 +00:00
|
|
|
if (cfg->log_libvirtd) {
|
2012-07-13 11:39:29 +00:00
|
|
|
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);
|
2013-05-21 08:03:33 +00:00
|
|
|
for (i = 0; i < nttyFDs; i++) {
|
2012-07-13 11:39:29 +00:00
|
|
|
virCommandAddArg(cmd, "--console");
|
|
|
|
virCommandAddArgFormat(cmd, "%d", ttyFDs[i]);
|
2013-07-11 10:31:56 +00:00
|
|
|
virCommandPassFD(cmd, ttyFDs[i], 0);
|
2012-07-13 11:39:29 +00:00
|
|
|
}
|
|
|
|
|
2013-07-09 17:15:45 +00:00
|
|
|
for (i = 0; i < nfiles; i++) {
|
|
|
|
virCommandAddArg(cmd, "--passfd");
|
|
|
|
virCommandAddArgFormat(cmd, "%d", files[i]);
|
2013-07-11 10:31:56 +00:00
|
|
|
virCommandPassFD(cmd, files[i], 0);
|
2013-07-09 17:15:45 +00:00
|
|
|
}
|
|
|
|
|
2015-08-20 13:46:17 +00:00
|
|
|
for (i = 0; i < VIR_LXC_DOMAIN_NAMESPACE_LAST; i++) {
|
|
|
|
if (nsInheritFDs[i] > 0) {
|
|
|
|
char *tmp = NULL;
|
|
|
|
if (virAsprintf(&tmp, "--share-%s",
|
|
|
|
nsInfoLocal[i]) < 0)
|
|
|
|
goto cleanup;
|
|
|
|
virCommandAddArg(cmd, tmp);
|
|
|
|
virCommandAddArgFormat(cmd, "%d", nsInheritFDs[i]);
|
|
|
|
virCommandPassFD(cmd, nsInheritFDs[i], 0);
|
|
|
|
VIR_FREE(tmp);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2012-07-13 11:39:29 +00:00
|
|
|
virCommandAddArgPair(cmd, "--security",
|
|
|
|
virSecurityManagerGetModel(driver->securityManager));
|
|
|
|
|
|
|
|
virCommandAddArg(cmd, "--handshake");
|
|
|
|
virCommandAddArgFormat(cmd, "%d", handshakefd);
|
|
|
|
|
2014-11-13 14:27:11 +00:00
|
|
|
for (i = 0; i < nveths; i++)
|
2012-07-13 11:39:29 +00:00
|
|
|
virCommandAddArgList(cmd, "--veth", veths[i], NULL);
|
|
|
|
|
2013-07-11 10:31:56 +00:00
|
|
|
virCommandPassFD(cmd, handshakefd, 0);
|
2015-01-16 15:03:16 +00:00
|
|
|
virCommandDaemonize(cmd);
|
|
|
|
virCommandSetPidFile(cmd, pidfile);
|
lxc: Don't pass a local variable address randomly
So, recently I was testing the LXC driver. You know, startup some
domains. But to my surprise, I was not able to start a single one:
virsh # start --console test
error: Reconnected to the hypervisor
error: Failed to start domain test
error: internal error: guest failed to start: unexpected exit status 125
So I've start digging. It turns out, that in virExec(), when I printed
out the @cmd, I got strange values: *(cmd->outfdptr) was certainly not
valid FD number: it has random value of several millions. This
obviously made prepareStdFd(childout, STDOUT_FILENO) fail (line 611).
But outfdptr is set in virCommandSetOutputFD(). The only place within
LXC driver where the function is called is in
virLXCProcessBuildControllerCmd(). If you take a closer look at the
function it looks like this:
static virCommandPtr
virLXCProcessBuildControllerCmd(virLXCDriverPtr driver,
..
int logfd,
const char *pidfile)
{
...
virCommandSetOutputFD(cmd, &logfd);
virCommandSetErrorFD(cmd, &logfd);
...
}
Yes, you guessed it. @logfd is passed into the function by value.
However, in the function we try to get its address (an address of a
local variable) which is no longer valid once function is finished and
stack is cleaned. Therefore when cmd->outfdptr is evaluated at any
point after this function, we may get a random number, depending on
what's currently on the stack. Of course, this may work sometimes too
- it depends on the compiler how it arranges the code, when the stack
is wiped out.
In order to fix this, lets pass a pointer to @logfd instead of
figuring out (wrong) its value in a function.
The bug was introduced in e1de5521.
Signed-off-by: Michal Privoznik <mprivozn@redhat.com>
2015-07-01 15:21:28 +00:00
|
|
|
virCommandSetOutputFD(cmd, logfd);
|
|
|
|
virCommandSetErrorFD(cmd, logfd);
|
2015-01-16 16:39:57 +00:00
|
|
|
/* So we can pause before exec'ing the controller to
|
|
|
|
* write the live domain status XML with the PID */
|
|
|
|
virCommandRequireHandshake(cmd);
|
2012-07-13 11:39:29 +00:00
|
|
|
|
|
|
|
return cmd;
|
2014-03-25 06:49:26 +00:00
|
|
|
cleanup:
|
2012-07-13 11:39:29 +00:00
|
|
|
virCommandFree(cmd);
|
2013-07-16 15:45:05 +00:00
|
|
|
virObjectUnref(cfg);
|
2012-07-13 11:39:29 +00:00
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
2013-03-04 20:47:37 +00:00
|
|
|
|
2013-10-14 12:07:22 +00:00
|
|
|
static bool
|
|
|
|
virLXCProcessIgnorableLogLine(const char *str)
|
|
|
|
{
|
|
|
|
if (virLogProbablyLogMessage(str))
|
|
|
|
return true;
|
|
|
|
if (strstr(str, "PATH="))
|
|
|
|
return true;
|
|
|
|
if (strstr(str, "error receiving signal from container"))
|
|
|
|
return true;
|
|
|
|
if (STREQ(str, ""))
|
|
|
|
return true;
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2012-07-13 11:39:29 +00:00
|
|
|
static int
|
2013-03-04 20:47:37 +00:00
|
|
|
virLXCProcessReadLogOutputData(virDomainObjPtr vm,
|
|
|
|
int fd,
|
|
|
|
char *buf,
|
|
|
|
size_t buflen)
|
2012-07-13 11:39:29 +00:00
|
|
|
{
|
|
|
|
int retries = 10;
|
2013-03-04 20:47:37 +00:00
|
|
|
int got = 0;
|
|
|
|
int ret = -1;
|
|
|
|
char *filter_next = buf;
|
2012-07-13 11:39:29 +00:00
|
|
|
|
2013-03-04 20:47:37 +00:00
|
|
|
buf[0] = '\0';
|
2012-07-13 11:39:29 +00:00
|
|
|
|
|
|
|
while (retries) {
|
|
|
|
ssize_t bytes;
|
2013-05-24 10:14:02 +00:00
|
|
|
bool isdead = false;
|
2013-03-04 20:47:37 +00:00
|
|
|
char *eol;
|
2012-07-13 11:39:29 +00:00
|
|
|
|
2013-03-04 20:47:37 +00:00
|
|
|
if (vm->pid <= 0 ||
|
|
|
|
(kill(vm->pid, 0) == -1 && errno == ESRCH))
|
2013-05-24 10:14:02 +00:00
|
|
|
isdead = true;
|
2012-07-13 11:39:29 +00:00
|
|
|
|
|
|
|
/* 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",
|
2013-03-04 20:47:37 +00:00
|
|
|
_("Failure while reading log output"));
|
2012-07-13 11:39:29 +00:00
|
|
|
goto cleanup;
|
|
|
|
}
|
|
|
|
|
|
|
|
got += bytes;
|
|
|
|
buf[got] = '\0';
|
|
|
|
|
2013-03-04 20:47:37 +00:00
|
|
|
/* Filter out debug messages from intermediate libvirt process */
|
|
|
|
while ((eol = strchr(filter_next, '\n'))) {
|
|
|
|
*eol = '\0';
|
2013-10-14 12:07:22 +00:00
|
|
|
if (virLXCProcessIgnorableLogLine(filter_next)) {
|
2013-03-04 20:47:37 +00:00
|
|
|
memmove(filter_next, eol + 1, got - (eol - buf));
|
|
|
|
got -= eol + 1 - filter_next;
|
|
|
|
} else {
|
|
|
|
filter_next = eol + 1;
|
|
|
|
*eol = '\n';
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (got == buflen-1) {
|
|
|
|
virReportError(VIR_ERR_INTERNAL_ERROR,
|
|
|
|
_("Out of space while reading log output: %s"),
|
|
|
|
buf);
|
|
|
|
goto cleanup;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (isdead) {
|
|
|
|
ret = got;
|
|
|
|
goto cleanup;
|
2012-07-13 11:39:29 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
usleep(100*1000);
|
|
|
|
retries--;
|
|
|
|
}
|
|
|
|
|
2013-03-04 20:47:37 +00:00
|
|
|
virReportError(VIR_ERR_INTERNAL_ERROR,
|
|
|
|
_("Timed out while reading log output: %s"),
|
|
|
|
buf);
|
2012-07-13 11:39:29 +00:00
|
|
|
|
2014-03-25 06:49:26 +00:00
|
|
|
cleanup:
|
2013-03-04 20:47:37 +00:00
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static int
|
|
|
|
virLXCProcessReadLogOutput(virDomainObjPtr vm,
|
|
|
|
char *logfile,
|
|
|
|
off_t pos,
|
|
|
|
char *buf,
|
|
|
|
size_t buflen)
|
|
|
|
{
|
|
|
|
int fd = -1;
|
|
|
|
int ret;
|
|
|
|
|
|
|
|
if ((fd = open(logfile, O_RDONLY)) < 0) {
|
|
|
|
virReportSystemError(errno,
|
|
|
|
_("Unable to open log file %s"),
|
|
|
|
logfile);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (lseek(fd, pos, SEEK_SET) < 0) {
|
|
|
|
virReportSystemError(errno,
|
|
|
|
_("Unable to seek log file %s to %llu"),
|
|
|
|
logfile, (unsigned long long)pos);
|
|
|
|
VIR_FORCE_CLOSE(fd);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
ret = virLXCProcessReadLogOutputData(vm,
|
|
|
|
fd,
|
|
|
|
buf,
|
|
|
|
buflen);
|
|
|
|
|
2012-07-13 11:39:29 +00:00
|
|
|
VIR_FORCE_CLOSE(fd);
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2013-04-03 15:19:24 +00:00
|
|
|
|
|
|
|
static int
|
|
|
|
virLXCProcessEnsureRootFS(virDomainObjPtr vm)
|
|
|
|
{
|
2013-11-12 11:57:56 +00:00
|
|
|
virDomainFSDefPtr root = virDomainGetFilesystemForTarget(vm->def, "/");
|
2013-04-03 15:19:24 +00:00
|
|
|
|
|
|
|
if (root)
|
|
|
|
return 0;
|
|
|
|
|
2016-07-19 07:27:32 +00:00
|
|
|
if (!(root = virDomainFSDefNew()))
|
2013-07-04 10:11:37 +00:00
|
|
|
goto error;
|
2013-04-03 15:19:24 +00:00
|
|
|
|
|
|
|
root->type = VIR_DOMAIN_FS_TYPE_MOUNT;
|
|
|
|
|
2016-07-14 13:52:38 +00:00
|
|
|
if (VIR_STRDUP(root->src->path, "/") < 0 ||
|
2013-05-03 12:43:39 +00:00
|
|
|
VIR_STRDUP(root->dst, "/") < 0)
|
|
|
|
goto error;
|
2013-04-03 15:19:24 +00:00
|
|
|
|
2013-07-04 10:11:37 +00:00
|
|
|
if (VIR_INSERT_ELEMENT(vm->def->fss,
|
|
|
|
0,
|
|
|
|
vm->def->nfss,
|
|
|
|
root) < 0)
|
|
|
|
goto error;
|
2013-04-03 15:19:24 +00:00
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
2014-03-25 06:49:26 +00:00
|
|
|
error:
|
2013-04-03 15:19:24 +00:00
|
|
|
virDomainFSDefFree(root);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
2012-07-13 11:39:29 +00:00
|
|
|
/**
|
2012-07-13 11:49:24 +00:00
|
|
|
* virLXCProcessStart:
|
2012-07-13 11:39:29 +00:00
|
|
|
* @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
|
|
|
|
*/
|
2012-07-13 11:49:24 +00:00
|
|
|
int virLXCProcessStart(virConnectPtr conn,
|
2012-07-13 11:56:29 +00:00
|
|
|
virLXCDriverPtr driver,
|
2012-07-13 11:49:24 +00:00
|
|
|
virDomainObjPtr vm,
|
2013-07-09 17:15:45 +00:00
|
|
|
unsigned int nfiles, int *files,
|
2012-07-13 11:49:24 +00:00
|
|
|
bool autoDestroy,
|
|
|
|
virDomainRunningReason reason)
|
2012-07-13 11:39:29 +00:00
|
|
|
{
|
|
|
|
int rc = -1, r;
|
|
|
|
size_t nttyFDs = 0;
|
|
|
|
int *ttyFDs = NULL;
|
|
|
|
size_t i;
|
|
|
|
char *logfile = NULL;
|
|
|
|
int logfd = -1;
|
2012-11-27 12:55:02 +00:00
|
|
|
size_t nveths = 0;
|
2012-07-13 11:39:29 +00:00
|
|
|
char **veths = NULL;
|
|
|
|
int handshakefds[2] = { -1, -1 };
|
|
|
|
off_t pos = -1;
|
|
|
|
char ebuf[1024];
|
|
|
|
char *timestamp;
|
2015-08-20 13:46:17 +00:00
|
|
|
int nsInheritFDs[VIR_LXC_DOMAIN_NAMESPACE_LAST];
|
2012-07-13 11:39:29 +00:00
|
|
|
virCommandPtr cmd = NULL;
|
2012-07-18 16:20:46 +00:00
|
|
|
virLXCDomainObjPrivatePtr priv = vm->privateData;
|
2013-07-15 09:43:10 +00:00
|
|
|
virCapsPtr caps = NULL;
|
2012-07-13 11:39:29 +00:00
|
|
|
virErrorPtr err = NULL;
|
2013-07-16 15:45:05 +00:00
|
|
|
virLXCDriverConfigPtr cfg = virLXCDriverGetConfig(driver);
|
2013-07-22 15:36:06 +00:00
|
|
|
virCgroupPtr selfcgroup;
|
2013-10-11 16:30:53 +00:00
|
|
|
int status;
|
2015-01-16 11:54:30 +00:00
|
|
|
char *pidfile = NULL;
|
2015-02-06 13:13:43 +00:00
|
|
|
bool clearSeclabel = false;
|
2015-02-04 13:42:56 +00:00
|
|
|
bool need_stop = false;
|
2012-07-13 11:39:29 +00:00
|
|
|
|
2013-07-22 15:36:06 +00:00
|
|
|
if (virCgroupNewSelf(&selfcgroup) < 0)
|
2012-07-13 11:39:29 +00:00
|
|
|
return -1;
|
|
|
|
|
2013-07-22 15:36:06 +00:00
|
|
|
if (!virCgroupHasController(selfcgroup,
|
2013-03-21 14:40:29 +00:00
|
|
|
VIR_CGROUP_CONTROLLER_CPUACCT)) {
|
2013-07-22 15:36:06 +00:00
|
|
|
virCgroupFree(&selfcgroup);
|
2012-07-13 12:59:51 +00:00
|
|
|
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
|
|
|
|
_("Unable to find 'cpuacct' cgroups controller mount"));
|
2012-07-13 11:39:29 +00:00
|
|
|
return -1;
|
|
|
|
}
|
2013-07-22 15:36:06 +00:00
|
|
|
if (!virCgroupHasController(selfcgroup,
|
2013-03-21 13:38:31 +00:00
|
|
|
VIR_CGROUP_CONTROLLER_DEVICES)) {
|
2013-07-22 15:36:06 +00:00
|
|
|
virCgroupFree(&selfcgroup);
|
2012-07-13 12:59:51 +00:00
|
|
|
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
|
|
|
|
_("Unable to find 'devices' cgroups controller mount"));
|
2012-07-13 11:39:29 +00:00
|
|
|
return -1;
|
|
|
|
}
|
2013-07-22 15:36:06 +00:00
|
|
|
if (!virCgroupHasController(selfcgroup,
|
2013-03-21 13:38:31 +00:00
|
|
|
VIR_CGROUP_CONTROLLER_MEMORY)) {
|
2013-07-22 15:36:06 +00:00
|
|
|
virCgroupFree(&selfcgroup);
|
2012-07-13 12:59:51 +00:00
|
|
|
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
|
|
|
|
_("Unable to find 'memory' cgroups controller mount"));
|
2012-07-13 11:39:29 +00:00
|
|
|
return -1;
|
|
|
|
}
|
2013-07-22 15:36:06 +00:00
|
|
|
virCgroupFree(&selfcgroup);
|
2012-07-13 11:39:29 +00:00
|
|
|
|
2015-02-12 19:43:26 +00:00
|
|
|
if (vm->def->nconsoles == 0) {
|
|
|
|
virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
|
|
|
|
_("At least one PTY console is required"));
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
for (i = 0; i < vm->def->nconsoles; i++) {
|
2016-10-21 11:45:54 +00:00
|
|
|
if (vm->def->consoles[i]->source->type != VIR_DOMAIN_CHR_TYPE_PTY) {
|
2015-02-12 19:43:26 +00:00
|
|
|
virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
|
|
|
|
_("Only PTY console types are supported"));
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2013-07-16 15:45:05 +00:00
|
|
|
if (virFileMakePath(cfg->logDir) < 0) {
|
2012-07-13 11:39:29 +00:00
|
|
|
virReportSystemError(errno,
|
|
|
|
_("Cannot create log directory '%s'"),
|
2013-07-16 15:45:05 +00:00
|
|
|
cfg->logDir);
|
2012-07-13 11:39:29 +00:00
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
2013-07-26 14:59:16 +00:00
|
|
|
if (!vm->def->resource) {
|
|
|
|
virDomainResourceDefPtr res;
|
|
|
|
|
|
|
|
if (VIR_ALLOC(res) < 0)
|
|
|
|
goto cleanup;
|
|
|
|
|
|
|
|
if (VIR_STRDUP(res->partition, "/machine") < 0) {
|
|
|
|
VIR_FREE(res);
|
|
|
|
goto cleanup;
|
|
|
|
}
|
|
|
|
|
|
|
|
vm->def->resource = res;
|
|
|
|
}
|
|
|
|
|
2012-07-13 11:39:29 +00:00
|
|
|
if (virAsprintf(&logfile, "%s/%s.log",
|
2013-07-16 15:45:05 +00:00
|
|
|
cfg->logDir, vm->def->name) < 0)
|
2015-01-16 11:54:30 +00:00
|
|
|
goto cleanup;
|
|
|
|
|
|
|
|
if (!(pidfile = virPidFileBuildPath(cfg->stateDir, vm->def->name)))
|
|
|
|
goto cleanup;
|
2012-07-13 11:39:29 +00:00
|
|
|
|
2013-07-15 09:43:10 +00:00
|
|
|
if (!(caps = virLXCDriverGetCapabilities(driver, false)))
|
|
|
|
goto cleanup;
|
|
|
|
|
2012-07-13 11:39:29 +00:00
|
|
|
/* 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");
|
2016-05-27 12:00:34 +00:00
|
|
|
if (virDomainObjSetDefTransient(caps, driver->xmlopt, vm) < 0)
|
2012-07-13 11:39:29 +00:00
|
|
|
goto cleanup;
|
|
|
|
|
|
|
|
/* Run an early hook to set-up missing devices */
|
|
|
|
if (virHookPresent(VIR_HOOK_DRIVER_LXC)) {
|
2016-02-03 21:40:35 +00:00
|
|
|
char *xml = virDomainDefFormat(vm->def, driver->caps, 0);
|
2012-07-13 11:39:29 +00:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2013-04-03 15:19:24 +00:00
|
|
|
if (virLXCProcessEnsureRootFS(vm) < 0)
|
|
|
|
goto cleanup;
|
|
|
|
|
2012-11-23 14:46:18 +00:00
|
|
|
/* Must be run before security labelling */
|
|
|
|
VIR_DEBUG("Preparing host devices");
|
|
|
|
if (virLXCPrepareHostDevices(driver, vm->def) < 0)
|
|
|
|
goto cleanup;
|
|
|
|
|
2012-07-13 11:39:29 +00:00
|
|
|
/* 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;
|
2013-07-04 10:11:37 +00:00
|
|
|
if (VIR_ALLOC_N(ttyFDs, nttyFDs) < 0)
|
2012-07-13 11:39:29 +00:00
|
|
|
goto cleanup;
|
2013-05-21 08:03:33 +00:00
|
|
|
for (i = 0; i < vm->def->nconsoles; i++)
|
2013-03-13 17:30:31 +00:00
|
|
|
ttyFDs[i] = -1;
|
2012-07-13 11:39:29 +00:00
|
|
|
|
|
|
|
/* If you are using a SecurityDriver with dynamic labelling,
|
|
|
|
then generate a security label for isolation */
|
|
|
|
VIR_DEBUG("Generating domain security label (if required)");
|
2015-02-06 13:13:43 +00:00
|
|
|
|
|
|
|
clearSeclabel = vm->def->nseclabels == 0 ||
|
|
|
|
vm->def->seclabels[0]->type == VIR_DOMAIN_SECLABEL_DEFAULT;
|
|
|
|
|
2012-08-15 22:10:35 +00:00
|
|
|
if (vm->def->nseclabels &&
|
|
|
|
vm->def->seclabels[0]->type == VIR_DOMAIN_SECLABEL_DEFAULT)
|
|
|
|
vm->def->seclabels[0]->type = VIR_DOMAIN_SECLABEL_NONE;
|
2012-07-13 11:39:29 +00:00
|
|
|
|
2015-02-12 17:32:40 +00:00
|
|
|
if (virSecurityManagerCheckAllLabel(driver->securityManager, vm->def) < 0)
|
|
|
|
goto cleanup;
|
|
|
|
|
2012-07-13 11:39:29 +00:00
|
|
|
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,
|
2017-05-29 12:27:51 +00:00
|
|
|
vm->def, NULL, false) < 0)
|
2012-07-13 11:39:29 +00:00
|
|
|
goto cleanup;
|
|
|
|
|
2015-02-12 19:32:39 +00:00
|
|
|
VIR_DEBUG("Setting up consoles");
|
2013-05-21 08:03:33 +00:00
|
|
|
for (i = 0; i < vm->def->nconsoles; i++) {
|
2012-07-13 11:39:29 +00:00
|
|
|
char *ttyPath;
|
|
|
|
|
|
|
|
if (virFileOpenTty(&ttyFDs[i], &ttyPath, 1) < 0) {
|
|
|
|
virReportSystemError(errno, "%s",
|
|
|
|
_("Failed to allocate tty"));
|
|
|
|
goto cleanup;
|
|
|
|
}
|
|
|
|
|
2016-10-21 11:45:54 +00:00
|
|
|
VIR_FREE(vm->def->consoles[i]->source->data.file.path);
|
|
|
|
vm->def->consoles[i]->source->data.file.path = ttyPath;
|
2012-07-13 11:39:29 +00:00
|
|
|
|
|
|
|
VIR_FREE(vm->def->consoles[i]->info.alias);
|
2013-07-04 10:11:37 +00:00
|
|
|
if (virAsprintf(&vm->def->consoles[i]->info.alias, "console%zu", i) < 0)
|
2012-07-13 11:39:29 +00:00
|
|
|
goto cleanup;
|
|
|
|
}
|
|
|
|
|
2015-02-12 19:32:39 +00:00
|
|
|
VIR_DEBUG("Setting up Interfaces");
|
2012-11-23 16:38:01 +00:00
|
|
|
if (virLXCProcessSetupInterfaces(conn, vm->def, &nveths, &veths) < 0)
|
2012-07-13 11:39:29 +00:00
|
|
|
goto cleanup;
|
|
|
|
|
2015-08-20 13:46:17 +00:00
|
|
|
VIR_DEBUG("Setting up namespaces if any");
|
|
|
|
if (virLXCProcessSetupNamespaces(conn, vm->def->namespaceData, nsInheritFDs) < 0)
|
|
|
|
goto cleanup;
|
|
|
|
|
2015-02-12 19:32:39 +00:00
|
|
|
VIR_DEBUG("Preparing to launch");
|
2012-07-13 11:39:29 +00:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2012-07-13 11:49:24 +00:00
|
|
|
if (!(cmd = virLXCProcessBuildControllerCmd(driver,
|
|
|
|
vm,
|
|
|
|
nveths, veths,
|
|
|
|
ttyFDs, nttyFDs,
|
2015-08-20 13:46:17 +00:00
|
|
|
nsInheritFDs,
|
2013-07-09 17:15:45 +00:00
|
|
|
files, nfiles,
|
2015-01-16 15:03:16 +00:00
|
|
|
handshakefds[1],
|
lxc: Don't pass a local variable address randomly
So, recently I was testing the LXC driver. You know, startup some
domains. But to my surprise, I was not able to start a single one:
virsh # start --console test
error: Reconnected to the hypervisor
error: Failed to start domain test
error: internal error: guest failed to start: unexpected exit status 125
So I've start digging. It turns out, that in virExec(), when I printed
out the @cmd, I got strange values: *(cmd->outfdptr) was certainly not
valid FD number: it has random value of several millions. This
obviously made prepareStdFd(childout, STDOUT_FILENO) fail (line 611).
But outfdptr is set in virCommandSetOutputFD(). The only place within
LXC driver where the function is called is in
virLXCProcessBuildControllerCmd(). If you take a closer look at the
function it looks like this:
static virCommandPtr
virLXCProcessBuildControllerCmd(virLXCDriverPtr driver,
..
int logfd,
const char *pidfile)
{
...
virCommandSetOutputFD(cmd, &logfd);
virCommandSetErrorFD(cmd, &logfd);
...
}
Yes, you guessed it. @logfd is passed into the function by value.
However, in the function we try to get its address (an address of a
local variable) which is no longer valid once function is finished and
stack is cleaned. Therefore when cmd->outfdptr is evaluated at any
point after this function, we may get a random number, depending on
what's currently on the stack. Of course, this may work sometimes too
- it depends on the compiler how it arranges the code, when the stack
is wiped out.
In order to fix this, lets pass a pointer to @logfd instead of
figuring out (wrong) its value in a function.
The bug was introduced in e1de5521.
Signed-off-by: Michal Privoznik <mprivozn@redhat.com>
2015-07-01 15:21:28 +00:00
|
|
|
&logfd,
|
2015-01-16 15:03:16 +00:00
|
|
|
pidfile)))
|
2012-07-13 11:39:29 +00:00
|
|
|
goto cleanup;
|
|
|
|
|
|
|
|
/* now that we know it is about to start call the hook if present */
|
|
|
|
if (virHookPresent(VIR_HOOK_DRIVER_LXC)) {
|
2016-02-03 21:40:35 +00:00
|
|
|
char *xml = virDomainDefFormat(vm->def, driver->caps, 0);
|
2012-07-13 11:39:29 +00:00
|
|
|
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 */
|
2013-07-04 10:11:37 +00:00
|
|
|
if ((timestamp = virTimeStringNow()) == NULL)
|
2012-07-13 11:39:29 +00:00
|
|
|
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)));
|
|
|
|
|
2015-02-12 19:32:39 +00:00
|
|
|
VIR_DEBUG("Launching container");
|
2014-02-20 00:32:19 +00:00
|
|
|
virCommandRawStatus(cmd);
|
2013-10-11 16:30:53 +00:00
|
|
|
if (virCommandRun(cmd, &status) < 0)
|
2012-07-13 11:39:29 +00:00
|
|
|
goto cleanup;
|
|
|
|
|
2013-10-11 16:30:53 +00:00
|
|
|
if (status != 0) {
|
2014-02-20 00:32:19 +00:00
|
|
|
if (virLXCProcessReadLogOutput(vm, logfile, pos, ebuf,
|
|
|
|
sizeof(ebuf)) <= 0) {
|
|
|
|
if (WIFEXITED(status))
|
|
|
|
snprintf(ebuf, sizeof(ebuf), _("unexpected exit status %d"),
|
|
|
|
WEXITSTATUS(status));
|
|
|
|
else
|
|
|
|
snprintf(ebuf, sizeof(ebuf), "%s", _("terminated abnormally"));
|
|
|
|
}
|
2013-10-11 16:30:53 +00:00
|
|
|
virReportError(VIR_ERR_INTERNAL_ERROR,
|
|
|
|
_("guest failed to start: %s"), ebuf);
|
|
|
|
goto cleanup;
|
|
|
|
}
|
|
|
|
|
2015-01-16 15:03:16 +00:00
|
|
|
/* It has started running, so get its pid */
|
2015-01-16 11:54:30 +00:00
|
|
|
if ((r = virPidFileReadPath(pidfile, &vm->pid)) < 0) {
|
2013-10-11 16:30:53 +00:00
|
|
|
if (virLXCProcessReadLogOutput(vm, logfile, pos, ebuf, sizeof(ebuf)) > 0)
|
2013-03-04 20:47:37 +00:00
|
|
|
virReportError(VIR_ERR_INTERNAL_ERROR,
|
2013-10-11 16:30:53 +00:00
|
|
|
_("guest failed to start: %s"), ebuf);
|
2013-03-04 20:47:37 +00:00
|
|
|
else
|
|
|
|
virReportSystemError(-r,
|
2015-01-16 11:54:30 +00:00
|
|
|
_("Failed to read pid file %s"),
|
|
|
|
pidfile);
|
2012-07-13 11:39:29 +00:00
|
|
|
goto cleanup;
|
|
|
|
}
|
|
|
|
|
2015-02-04 13:42:56 +00:00
|
|
|
need_stop = true;
|
2012-07-17 14:54:08 +00:00
|
|
|
priv->stopReason = VIR_DOMAIN_EVENT_STOPPED_FAILED;
|
2012-07-23 11:25:37 +00:00
|
|
|
priv->wantReboot = false;
|
2012-07-13 11:39:29 +00:00
|
|
|
vm->def->id = vm->pid;
|
|
|
|
virDomainObjSetState(vm, VIR_DOMAIN_RUNNING, reason);
|
2012-07-17 10:55:38 +00:00
|
|
|
priv->doneStopEvent = false;
|
2012-07-13 11:39:29 +00:00
|
|
|
|
2015-01-16 15:03:16 +00:00
|
|
|
if (VIR_CLOSE(handshakefds[1]) < 0) {
|
|
|
|
virReportSystemError(errno, "%s", _("could not close handshake fd"));
|
2015-02-04 13:42:56 +00:00
|
|
|
goto cleanup;
|
2015-01-16 16:39:57 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
if (virCommandHandshakeWait(cmd) < 0)
|
2015-02-04 13:42:56 +00:00
|
|
|
goto cleanup;
|
2015-01-16 16:39:57 +00:00
|
|
|
|
|
|
|
/* Write domain status to disk for the controller to
|
|
|
|
* read when it starts */
|
2016-02-04 12:32:45 +00:00
|
|
|
if (virDomainSaveStatus(driver->xmlopt, cfg->stateDir, vm, driver->caps) < 0)
|
2015-02-04 13:42:56 +00:00
|
|
|
goto cleanup;
|
2015-01-16 16:39:57 +00:00
|
|
|
|
|
|
|
/* Allow the child to exec the controller */
|
|
|
|
if (virCommandHandshakeNotify(cmd) < 0)
|
2015-02-04 13:42:56 +00:00
|
|
|
goto cleanup;
|
2015-01-16 16:39:57 +00:00
|
|
|
|
2013-07-16 17:05:06 +00:00
|
|
|
if (virAtomicIntInc(&driver->nactive) == 1 && driver->inhibitCallback)
|
2012-10-31 19:03:55 +00:00
|
|
|
driver->inhibitCallback(true, driver->inhibitOpaque);
|
|
|
|
|
2012-07-13 11:39:29 +00:00
|
|
|
if (lxcContainerWaitForContinue(handshakefds[0]) < 0) {
|
|
|
|
char out[1024];
|
|
|
|
|
2012-07-13 11:49:24 +00:00
|
|
|
if (!(virLXCProcessReadLogOutput(vm, logfile, pos, out, 1024) < 0)) {
|
2012-07-13 12:59:51 +00:00
|
|
|
virReportError(VIR_ERR_INTERNAL_ERROR,
|
|
|
|
_("guest failed to start: %s"), out);
|
2012-07-13 11:39:29 +00:00
|
|
|
}
|
|
|
|
|
2015-02-04 13:42:56 +00:00
|
|
|
goto cleanup;
|
2012-07-13 11:39:29 +00:00
|
|
|
}
|
|
|
|
|
2015-01-16 15:03:16 +00:00
|
|
|
/* We know the cgroup must exist by this synchronization
|
|
|
|
* point so lets detect that first, since it gives us a
|
|
|
|
* more reliable way to kill everything off if something
|
|
|
|
* goes wrong from here onwards ... */
|
2016-02-01 15:50:54 +00:00
|
|
|
if (virCgroupNewDetectMachine(vm->def->name, "lxc",
|
|
|
|
vm->def->id, true,
|
|
|
|
vm->pid, -1, &priv->cgroup) < 0)
|
2015-02-04 13:42:56 +00:00
|
|
|
goto cleanup;
|
2015-01-16 15:03:16 +00:00
|
|
|
|
|
|
|
if (!priv->cgroup) {
|
|
|
|
virReportError(VIR_ERR_INTERNAL_ERROR,
|
|
|
|
_("No valid cgroup for machine %s"),
|
|
|
|
vm->def->name);
|
2015-02-04 13:42:56 +00:00
|
|
|
goto cleanup;
|
2015-01-16 15:03:16 +00:00
|
|
|
}
|
|
|
|
|
2016-02-01 15:50:54 +00:00
|
|
|
/* Get the machine name so we can properly delete it through
|
|
|
|
* systemd later */
|
|
|
|
if (!(priv->machineName = virSystemdGetMachineNameByPID(vm->pid)))
|
|
|
|
virResetLastError();
|
|
|
|
|
2015-01-16 15:03:16 +00:00
|
|
|
/* And we can get the first monitor connection now too */
|
|
|
|
if (!(priv->monitor = virLXCProcessConnectMonitor(driver, vm))) {
|
|
|
|
/* Intentionally overwrite the real monitor error message,
|
|
|
|
* since a better one is almost always found in the logs
|
|
|
|
*/
|
|
|
|
if (virLXCProcessReadLogOutput(vm, logfile, pos, ebuf, sizeof(ebuf)) > 0) {
|
|
|
|
virResetLastError();
|
|
|
|
virReportError(VIR_ERR_INTERNAL_ERROR,
|
|
|
|
_("guest failed to start: %s"), ebuf);
|
|
|
|
}
|
2015-02-04 13:42:56 +00:00
|
|
|
goto cleanup;
|
2015-01-16 15:03:16 +00:00
|
|
|
}
|
|
|
|
|
2012-07-13 11:39:29 +00:00
|
|
|
if (autoDestroy &&
|
2013-07-15 17:08:11 +00:00
|
|
|
virCloseCallbacksSet(driver->closeCallbacks, vm,
|
|
|
|
conn, lxcProcessAutoDestroy) < 0)
|
2015-02-04 13:42:56 +00:00
|
|
|
goto cleanup;
|
2012-07-13 11:39:29 +00:00
|
|
|
|
2014-06-27 08:41:22 +00:00
|
|
|
/* We don't need the temporary NIC names anymore, clear them */
|
|
|
|
virLXCProcessCleanInterfaces(vm->def);
|
|
|
|
|
2012-07-13 11:39:29 +00:00
|
|
|
/* finally we can call the 'started' hook script if any */
|
|
|
|
if (virHookPresent(VIR_HOOK_DRIVER_LXC)) {
|
2016-02-03 21:40:35 +00:00
|
|
|
char *xml = virDomainDefFormat(vm->def, driver->caps, 0);
|
2012-07-13 11:39:29 +00:00
|
|
|
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)
|
2015-02-04 13:42:56 +00:00
|
|
|
goto cleanup;
|
2012-07-13 11:39:29 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
rc = 0;
|
|
|
|
|
2014-03-25 06:49:26 +00:00
|
|
|
cleanup:
|
2012-07-13 11:39:29 +00:00
|
|
|
if (VIR_CLOSE(logfd) < 0) {
|
|
|
|
virReportSystemError(errno, "%s", _("could not close logfile"));
|
|
|
|
rc = -1;
|
|
|
|
}
|
|
|
|
if (rc != 0) {
|
2015-02-04 13:42:56 +00:00
|
|
|
err = virSaveLastError();
|
|
|
|
if (need_stop) {
|
|
|
|
virLXCProcessStop(driver, vm, VIR_DOMAIN_SHUTOFF_FAILED);
|
|
|
|
} else {
|
|
|
|
virSecurityManagerRestoreAllLabel(driver->securityManager,
|
2017-05-29 12:27:51 +00:00
|
|
|
vm->def, false, false);
|
2015-02-04 13:42:56 +00:00
|
|
|
virSecurityManagerReleaseLabel(driver->securityManager, vm->def);
|
|
|
|
/* Clear out dynamically assigned labels */
|
|
|
|
if (vm->def->nseclabels &&
|
|
|
|
(vm->def->seclabels[0]->type == VIR_DOMAIN_SECLABEL_DYNAMIC ||
|
|
|
|
clearSeclabel)) {
|
|
|
|
VIR_FREE(vm->def->seclabels[0]->model);
|
|
|
|
VIR_FREE(vm->def->seclabels[0]->label);
|
|
|
|
VIR_FREE(vm->def->seclabels[0]->imagelabel);
|
|
|
|
VIR_DELETE_ELEMENT(vm->def->seclabels, 0, vm->def->nseclabels);
|
|
|
|
}
|
|
|
|
virLXCProcessCleanup(driver, vm, VIR_DOMAIN_SHUTOFF_FAILED);
|
2012-07-13 11:39:29 +00:00
|
|
|
}
|
|
|
|
}
|
2015-02-04 13:42:56 +00:00
|
|
|
virCommandFree(cmd);
|
|
|
|
for (i = 0; i < nveths; i++)
|
|
|
|
VIR_FREE(veths[i]);
|
2013-05-21 08:03:33 +00:00
|
|
|
for (i = 0; i < nttyFDs; i++)
|
2012-07-13 11:39:29 +00:00
|
|
|
VIR_FORCE_CLOSE(ttyFDs[i]);
|
|
|
|
VIR_FREE(ttyFDs);
|
|
|
|
VIR_FORCE_CLOSE(handshakefds[0]);
|
|
|
|
VIR_FORCE_CLOSE(handshakefds[1]);
|
2015-01-16 11:54:30 +00:00
|
|
|
VIR_FREE(pidfile);
|
2012-07-13 11:39:29 +00:00
|
|
|
VIR_FREE(logfile);
|
2013-07-16 15:45:05 +00:00
|
|
|
virObjectUnref(cfg);
|
2013-07-15 09:43:10 +00:00
|
|
|
virObjectUnref(caps);
|
2012-07-13 11:39:29 +00:00
|
|
|
|
|
|
|
if (err) {
|
|
|
|
virSetError(err);
|
|
|
|
virFreeError(err);
|
|
|
|
}
|
|
|
|
|
|
|
|
return rc;
|
|
|
|
}
|
|
|
|
|
2012-07-13 11:49:24 +00:00
|
|
|
struct virLXCProcessAutostartData {
|
2012-07-13 11:56:29 +00:00
|
|
|
virLXCDriverPtr driver;
|
2012-07-13 11:39:29 +00:00
|
|
|
virConnectPtr conn;
|
|
|
|
};
|
|
|
|
|
2013-01-11 13:54:15 +00:00
|
|
|
static int
|
|
|
|
virLXCProcessAutostartDomain(virDomainObjPtr vm,
|
|
|
|
void *opaque)
|
2012-07-13 11:39:29 +00:00
|
|
|
{
|
2012-07-13 11:49:24 +00:00
|
|
|
const struct virLXCProcessAutostartData *data = opaque;
|
2013-01-11 13:54:15 +00:00
|
|
|
int ret = 0;
|
2012-07-13 11:39:29 +00:00
|
|
|
|
2013-01-09 21:00:32 +00:00
|
|
|
virObjectLock(vm);
|
2012-07-13 11:39:29 +00:00
|
|
|
if (vm->autostart &&
|
|
|
|
!virDomainObjIsActive(vm)) {
|
2013-07-09 17:15:45 +00:00
|
|
|
ret = virLXCProcessStart(data->conn, data->driver, vm,
|
|
|
|
0, NULL, false,
|
2013-01-11 13:54:15 +00:00
|
|
|
VIR_DOMAIN_RUNNING_BOOTED);
|
2012-07-13 11:39:29 +00:00
|
|
|
virDomainAuditStart(vm, "booted", ret >= 0);
|
|
|
|
if (ret < 0) {
|
|
|
|
VIR_ERROR(_("Failed to autostart VM '%s': %s"),
|
|
|
|
vm->def->name,
|
2016-05-19 19:10:19 +00:00
|
|
|
virGetLastErrorMessage());
|
2012-07-13 11:39:29 +00:00
|
|
|
} else {
|
2013-11-22 14:38:05 +00:00
|
|
|
virObjectEventPtr event =
|
2013-11-21 17:03:26 +00:00
|
|
|
virDomainEventLifecycleNewFromObj(vm,
|
2012-07-13 11:39:29 +00:00
|
|
|
VIR_DOMAIN_EVENT_STARTED,
|
|
|
|
VIR_DOMAIN_EVENT_STARTED_BOOTED);
|
|
|
|
if (event)
|
2013-11-21 10:43:10 +00:00
|
|
|
virObjectEventStateQueue(data->driver->domainEventState, event);
|
2012-07-13 11:39:29 +00:00
|
|
|
}
|
|
|
|
}
|
2013-01-09 21:00:32 +00:00
|
|
|
virObjectUnlock(vm);
|
2013-01-11 13:54:15 +00:00
|
|
|
return ret;
|
2012-07-13 11:39:29 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void
|
2012-07-13 11:56:29 +00:00
|
|
|
virLXCProcessAutostartAll(virLXCDriverPtr driver)
|
|
|
|
{
|
2012-07-13 11:39:29 +00:00
|
|
|
/* 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 */
|
|
|
|
|
2012-07-13 11:49:24 +00:00
|
|
|
struct virLXCProcessAutostartData data = { driver, conn };
|
2012-07-13 11:39:29 +00:00
|
|
|
|
2013-01-11 13:54:15 +00:00
|
|
|
virDomainObjListForEach(driver->domains,
|
|
|
|
virLXCProcessAutostartDomain,
|
|
|
|
&data);
|
2012-07-13 11:39:29 +00:00
|
|
|
|
2014-01-03 15:08:52 +00:00
|
|
|
virObjectUnref(conn);
|
2012-07-13 11:39:29 +00:00
|
|
|
}
|
|
|
|
|
2013-01-11 13:54:15 +00:00
|
|
|
static int
|
|
|
|
virLXCProcessReconnectDomain(virDomainObjPtr vm,
|
|
|
|
void *opaque)
|
2012-07-13 11:39:29 +00:00
|
|
|
{
|
2012-07-13 11:56:29 +00:00
|
|
|
virLXCDriverPtr driver = opaque;
|
2012-07-18 16:20:46 +00:00
|
|
|
virLXCDomainObjPrivatePtr priv;
|
2013-01-11 13:54:15 +00:00
|
|
|
int ret = -1;
|
2012-07-13 11:39:29 +00:00
|
|
|
|
2013-01-09 21:00:32 +00:00
|
|
|
virObjectLock(vm);
|
2012-11-23 11:23:32 +00:00
|
|
|
VIR_DEBUG("Reconnect id=%d pid=%d state=%d", vm->def->id, vm->pid, vm->state.state);
|
2012-07-13 11:39:29 +00:00
|
|
|
|
|
|
|
priv = vm->privateData;
|
|
|
|
|
|
|
|
if (vm->pid != 0) {
|
|
|
|
vm->def->id = vm->pid;
|
|
|
|
virDomainObjSetState(vm, VIR_DOMAIN_RUNNING,
|
|
|
|
VIR_DOMAIN_RUNNING_UNKNOWN);
|
|
|
|
|
2013-07-16 17:05:06 +00:00
|
|
|
if (virAtomicIntInc(&driver->nactive) == 1 && driver->inhibitCallback)
|
2012-10-31 19:03:55 +00:00
|
|
|
driver->inhibitCallback(true, driver->inhibitOpaque);
|
|
|
|
|
2012-07-17 10:55:38 +00:00
|
|
|
if (!(priv->monitor = virLXCProcessConnectMonitor(driver, vm)))
|
2012-07-13 11:39:29 +00:00
|
|
|
goto error;
|
|
|
|
|
2016-02-01 15:50:54 +00:00
|
|
|
if (virCgroupNewDetectMachine(vm->def->name, "lxc", vm->def->id, true,
|
|
|
|
vm->pid, -1, &priv->cgroup) < 0)
|
2013-03-21 14:40:29 +00:00
|
|
|
goto error;
|
|
|
|
|
2013-07-24 16:36:42 +00:00
|
|
|
if (!priv->cgroup) {
|
2013-07-22 12:59:28 +00:00
|
|
|
virReportError(VIR_ERR_INTERNAL_ERROR,
|
2013-07-24 16:36:42 +00:00
|
|
|
_("No valid cgroup for machine %s"),
|
2013-07-22 12:59:28 +00:00
|
|
|
vm->def->name);
|
|
|
|
goto error;
|
|
|
|
}
|
|
|
|
|
2016-02-01 15:50:54 +00:00
|
|
|
if (!(priv->machineName = virSystemdGetMachineNameByPID(vm->pid)))
|
|
|
|
virResetLastError();
|
|
|
|
|
2014-03-13 11:58:17 +00:00
|
|
|
if (virLXCUpdateActiveUSBHostdevs(driver, vm->def) < 0)
|
2012-11-23 14:46:18 +00:00
|
|
|
goto error;
|
|
|
|
|
2012-07-13 11:39:29 +00:00
|
|
|
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)) {
|
2016-02-03 21:40:35 +00:00
|
|
|
char *xml = virDomainDefFormat(vm->def, driver->caps, 0);
|
2012-07-13 11:39:29 +00:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2013-01-11 13:54:15 +00:00
|
|
|
ret = 0;
|
2014-03-25 06:49:26 +00:00
|
|
|
cleanup:
|
2013-01-09 21:00:32 +00:00
|
|
|
virObjectUnlock(vm);
|
2013-01-11 13:54:15 +00:00
|
|
|
return ret;
|
2012-07-13 11:39:29 +00:00
|
|
|
|
2014-03-25 06:49:26 +00:00
|
|
|
error:
|
2012-07-13 11:49:24 +00:00
|
|
|
virLXCProcessStop(driver, vm, VIR_DOMAIN_SHUTOFF_FAILED);
|
2012-07-13 11:39:29 +00:00
|
|
|
virDomainAuditStop(vm, "failed");
|
|
|
|
goto cleanup;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2012-07-13 11:56:29 +00:00
|
|
|
int virLXCProcessReconnectAll(virLXCDriverPtr driver,
|
2012-07-13 11:49:24 +00:00
|
|
|
virDomainObjListPtr doms)
|
2012-07-13 11:39:29 +00:00
|
|
|
{
|
2013-01-11 13:54:15 +00:00
|
|
|
virDomainObjListForEach(doms, virLXCProcessReconnectDomain, driver);
|
2012-07-13 11:39:29 +00:00
|
|
|
return 0;
|
|
|
|
}
|