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"
|
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-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"
|
2020-02-16 21:59:28 +00:00
|
|
|
#include "virutil.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"
|
|
|
|
|
2018-03-27 15:39:53 +00:00
|
|
|
static void
|
2013-07-15 17:08:11 +00:00
|
|
|
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
|
|
|
|
2018-03-27 15:39:53 +00:00
|
|
|
if (!dom->persistent)
|
2013-07-15 17:08:11 +00:00
|
|
|
virDomainObjListRemove(driver->domains, dom);
|
2012-07-13 11:39:29 +00:00
|
|
|
|
2018-06-11 19:38:17 +00:00
|
|
|
virObjectEventStateQueue(driver->domainEventState, event);
|
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 {
|
2018-03-27 13:32:07 +00:00
|
|
|
conn = virConnectOpen("lxc:///system");
|
2012-07-23 11:25:37 +00:00
|
|
|
/* 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)
|
|
|
|
{
|
2020-05-12 16:53:07 +00:00
|
|
|
g_autofree char *file = g_strdup_printf("%s/%s.xml",
|
|
|
|
cfg->stateDir,
|
|
|
|
vm->def->name);
|
2015-07-28 16:25:59 +00:00
|
|
|
|
|
|
|
if (unlink(file) < 0 && errno != ENOENT && errno != ENOTDIR)
|
|
|
|
VIR_WARN("Failed to remove domain XML for %s: %s",
|
2020-02-26 17:57:34 +00:00
|
|
|
vm->def->name, g_strerror(errno));
|
2015-07-28 16:25:59 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
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;
|
2019-10-01 17:56:35 +00:00
|
|
|
const virNetDevVPortProfile *vport = NULL;
|
2013-07-16 15:45:05 +00:00
|
|
|
virLXCDriverConfigPtr cfg = virLXCDriverGetConfig(driver);
|
2018-07-26 14:32:04 +00:00
|
|
|
virConnectPtr conn = NULL;
|
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)) {
|
2020-05-12 16:53:07 +00:00
|
|
|
g_autofree char *xml = virDomainDefFormat(vm->def, driver->xmlopt, 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);
|
|
|
|
}
|
|
|
|
|
2019-01-24 16:38:10 +00:00
|
|
|
virSecurityManagerRestoreAllLabel(driver->securityManager,
|
|
|
|
vm->def, false, false);
|
|
|
|
virSecurityManagerReleaseLabel(driver->securityManager, vm->def);
|
|
|
|
/* Clear out dynamically assigned labels */
|
|
|
|
if (vm->def->nseclabels &&
|
|
|
|
vm->def->seclabels[0]->type == VIR_DOMAIN_SECLABEL_DYNAMIC) {
|
2020-05-12 16:53:07 +00:00
|
|
|
g_free(vm->def->seclabels[0]->model);
|
|
|
|
g_free(vm->def->seclabels[0]->label);
|
|
|
|
g_free(vm->def->seclabels[0]->imagelabel);
|
|
|
|
vm->def->seclabels[0]->model = NULL;
|
|
|
|
vm->def->seclabels[0]->label = NULL;
|
|
|
|
vm->def->seclabels[0]->imagelabel = NULL;
|
2019-01-24 16:38:10 +00:00
|
|
|
}
|
|
|
|
|
2012-07-13 11:39:29 +00:00
|
|
|
/* 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;
|
|
|
|
|
2020-01-31 16:12:11 +00:00
|
|
|
if (!!g_atomic_int_dec_and_test(&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));
|
|
|
|
}
|
2018-07-26 14:32:04 +00:00
|
|
|
if (iface->type == VIR_DOMAIN_NET_TYPE_NETWORK) {
|
|
|
|
if (conn || (conn = virGetConnectNetwork()))
|
|
|
|
virDomainNetReleaseActualDevice(conn, vm->def, iface);
|
|
|
|
else
|
|
|
|
VIR_WARN("Unable to release network device '%s'", NULLSTR(iface->ifname));
|
|
|
|
}
|
2012-07-13 11:39:29 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
virDomainConfVMNWFilterTeardown(vm);
|
|
|
|
|
2013-03-21 14:40:29 +00:00
|
|
|
if (priv->cgroup) {
|
|
|
|
virCgroupRemove(priv->cgroup);
|
2018-07-30 09:06:31 +00:00
|
|
|
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);
|
2020-05-12 16:53:07 +00:00
|
|
|
g_free(priv->machineName);
|
|
|
|
priv->machineName = NULL;
|
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)) {
|
2020-05-12 16:53:07 +00:00
|
|
|
g_autofree char *xml = virDomainDefFormat(vm->def, driver->xmlopt, 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);
|
|
|
|
}
|
|
|
|
|
2016-09-08 13:16:58 +00:00
|
|
|
virDomainObjRemoveTransientDef(vm);
|
2013-07-16 15:45:05 +00:00
|
|
|
virObjectUnref(cfg);
|
2018-07-26 14:32:04 +00:00
|
|
|
virObjectUnref(conn);
|
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
|
|
|
{
|
|
|
|
char *parentVeth;
|
|
|
|
char *containerVeth = NULL;
|
2019-10-01 17:56:35 +00:00
|
|
|
const virNetDevVPortProfile *vport = virDomainNetGetActualVirtPortProfile(net);
|
2012-07-13 11:39:29 +00:00
|
|
|
|
|
|
|
VIR_DEBUG("calling vethCreate()");
|
|
|
|
parentVeth = net->ifname;
|
|
|
|
if (virNetDevVethCreate(&parentVeth, &containerVeth) < 0)
|
2019-10-21 18:18:55 +00:00
|
|
|
return NULL;
|
2012-07-13 11:39:29 +00:00
|
|
|
VIR_DEBUG("parentVeth: %s, containerVeth: %s", parentVeth, containerVeth);
|
|
|
|
|
|
|
|
if (net->ifname == NULL)
|
|
|
|
net->ifname = parentVeth;
|
|
|
|
|
|
|
|
if (virNetDevSetMAC(containerVeth, &net->mac) < 0)
|
2019-10-21 18:18:55 +00:00
|
|
|
return NULL;
|
2012-07-13 11:39:29 +00:00
|
|
|
|
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)
|
2019-10-21 18:18:55 +00:00
|
|
|
return NULL;
|
2016-05-13 17:20:54 +00:00
|
|
|
} else {
|
|
|
|
if (virNetDevBridgeAddPort(brname, parentVeth) < 0)
|
2019-10-21 18:18:55 +00:00
|
|
|
return NULL;
|
2020-02-13 17:57:47 +00:00
|
|
|
|
|
|
|
if (virDomainNetGetActualPortOptionsIsolated(net) == VIR_TRISTATE_BOOL_YES &&
|
|
|
|
virNetDevBridgePortSetIsolated(brname, parentVeth, true) < 0) {
|
|
|
|
virErrorPtr err;
|
|
|
|
|
|
|
|
virErrorPreserveLast(&err);
|
|
|
|
ignore_value(virNetDevBridgeRemovePort(brname, parentVeth));
|
|
|
|
virErrorRestore(&err);
|
|
|
|
return NULL;
|
|
|
|
}
|
2016-05-13 17:20:54 +00:00
|
|
|
}
|
2012-11-27 12:55:02 +00:00
|
|
|
}
|
2012-07-13 11:39:29 +00:00
|
|
|
|
|
|
|
if (virNetDevSetOnline(parentVeth, true) < 0)
|
2019-10-21 18:18:55 +00:00
|
|
|
return NULL;
|
2012-07-13 11:39:29 +00:00
|
|
|
|
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)
|
2019-10-21 18:18:55 +00:00
|
|
|
return NULL;
|
2016-06-17 18:52:48 +00:00
|
|
|
}
|
|
|
|
|
2012-07-13 11:39:29 +00:00
|
|
|
if (net->filter &&
|
2018-05-11 17:39:27 +00:00
|
|
|
virDomainConfNWFilterInstantiate(vm->name, vm->uuid, net, false) < 0)
|
2019-10-21 18:18:55 +00:00
|
|
|
return NULL;
|
2012-07-13 11:39:29 +00:00
|
|
|
|
2019-10-21 18:18:55 +00:00
|
|
|
return containerVeth;
|
2012-07-13 11:39:29 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2019-12-04 08:08:17 +00:00
|
|
|
char *
|
|
|
|
virLXCProcessSetupInterfaceDirect(virLXCDriverPtr driver,
|
|
|
|
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;
|
2019-10-01 16:52:03 +00:00
|
|
|
const virNetDevBandwidth *bw;
|
2019-10-01 17:56:35 +00:00
|
|
|
const virNetDevVPortProfile *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",
|
|
|
|
};
|
|
|
|
|
2019-12-04 08:08:17 +00:00
|
|
|
static int virLXCProcessSetupNamespaceName(virLXCDriverPtr driver,
|
|
|
|
int ns_type,
|
|
|
|
const char *name)
|
2015-08-20 13:46:17 +00:00
|
|
|
{
|
|
|
|
int fd = -1;
|
|
|
|
virDomainObjPtr vm;
|
|
|
|
virLXCDomainObjPrivatePtr priv;
|
2020-03-05 08:25:35 +00:00
|
|
|
g_autofree char *path = NULL;
|
2015-08-20 13:46:17 +00:00
|
|
|
|
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2019-10-22 13:26:14 +00:00
|
|
|
path = g_strdup_printf("/proc/%lld/ns/%s", (long long int)priv->initpid,
|
|
|
|
nsInfoLocal[ns_type]);
|
2015-08-20 13:46:17 +00:00
|
|
|
|
|
|
|
if ((fd = open(path, O_RDONLY)) < 0) {
|
|
|
|
virReportSystemError(errno,
|
|
|
|
_("failed to open ns %s"),
|
|
|
|
virLXCDomainNamespaceTypeToString(ns_type));
|
|
|
|
goto cleanup;
|
|
|
|
}
|
|
|
|
|
|
|
|
cleanup:
|
2018-03-10 13:47:44 +00:00
|
|
|
virDomainObjEndAPI(&vm);
|
2015-08-20 13:46:17 +00:00
|
|
|
return fd;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static int virLXCProcessSetupNamespacePID(int ns_type, const char *name)
|
|
|
|
{
|
2020-05-12 16:53:07 +00:00
|
|
|
g_autofree char *path = g_strdup_printf("/proc/%s/ns/%s",
|
|
|
|
name, nsInfoLocal[ns_type]);
|
|
|
|
int fd = open(path, O_RDONLY);
|
2015-08-20 13:46:17 +00:00
|
|
|
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)
|
|
|
|
{
|
2020-05-12 16:53:07 +00:00
|
|
|
g_autofree char *path = NULL;
|
2015-08-20 13:46:17 +00:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2019-10-22 13:26:14 +00:00
|
|
|
path = g_strdup_printf("%s/netns/%s", RUNSTATEDIR, name);
|
2015-08-20 13:46:17 +00:00
|
|
|
fd = open(path, O_RDONLY);
|
|
|
|
if (fd < 0) {
|
|
|
|
virReportSystemError(errno,
|
|
|
|
_("failed to open netns %s"), name);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
return fd;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
* virLXCProcessSetupNamespaces:
|
2019-12-04 08:08:17 +00:00
|
|
|
* @driver: pointer to driver structure
|
2015-08-20 13:46:17 +00:00
|
|
|
* @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
|
|
|
|
*/
|
2019-12-04 08:08:17 +00:00
|
|
|
static int
|
|
|
|
virLXCProcessSetupNamespaces(virLXCDriverPtr driver,
|
|
|
|
lxcDomainDefPtr lxcDef,
|
|
|
|
int *nsFDs)
|
2015-08-20 13:46:17 +00:00
|
|
|
{
|
|
|
|
size_t i;
|
|
|
|
|
|
|
|
for (i = 0; i < VIR_LXC_DOMAIN_NAMESPACE_LAST; i++)
|
|
|
|
nsFDs[i] = -1;
|
2020-01-24 20:30:04 +00:00
|
|
|
/* If there are no namespaces to be opened just return success */
|
2015-08-20 13:46:17 +00:00
|
|
|
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:
|
2019-12-04 08:08:17 +00:00
|
|
|
if ((nsFDs[i] = virLXCProcessSetupNamespaceName(driver, i,
|
|
|
|
lxcDef->ns_val[i])) < 0)
|
2015-08-20 13:46:17 +00:00
|
|
|
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:
|
2019-12-04 08:08:17 +00:00
|
|
|
* @driver: pointer to driver structure
|
2012-07-13 11:39:29 +00:00
|
|
|
* @def: pointer to virtual machine structure
|
2018-07-26 15:25:58 +00:00
|
|
|
* @veths: string list of interface names
|
2012-07-13 11:39:29 +00:00
|
|
|
*
|
|
|
|
* 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
|
|
|
|
*/
|
2019-12-04 08:08:17 +00:00
|
|
|
static int
|
|
|
|
virLXCProcessSetupInterfaces(virLXCDriverPtr driver,
|
|
|
|
virDomainDefPtr def,
|
|
|
|
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;
|
2018-07-26 14:32:04 +00:00
|
|
|
virConnectPtr netconn = NULL;
|
2019-04-30 14:17:14 +00:00
|
|
|
virErrorPtr save_err = NULL;
|
2012-07-13 11:39:29 +00:00
|
|
|
|
2020-05-12 17:31:16 +00:00
|
|
|
*veths = g_new0(char *, def->nnets + 1);
|
2018-07-26 15:25:58 +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;
|
2019-10-01 16:52:03 +00:00
|
|
|
const virNetDevBandwidth *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)
|
2018-07-27 12:32:36 +00:00
|
|
|
goto cleanup;
|
2016-05-13 17:20:54 +00:00
|
|
|
|
2018-07-26 14:32:04 +00:00
|
|
|
if (net->type == VIR_DOMAIN_NET_TYPE_NETWORK) {
|
|
|
|
if (!netconn && !(netconn = virGetConnectNetwork()))
|
|
|
|
goto cleanup;
|
|
|
|
if (virDomainNetAllocateActualDevice(netconn, def, net) < 0)
|
|
|
|
goto cleanup;
|
|
|
|
}
|
2012-07-13 11:39:29 +00:00
|
|
|
|
conf: add hypervisor agnostic, domain start-time, validation function for NetDef
<interface> devices (virDomainNetDef) are a bit different from other
types of devices in that their actual type may come from a network (in
the form of a port connection), and that doesn't happen until the
domain is started. This means that any validation of an <interface> at
parse time needs to be a bit liberal in what it accepts - when
type='network', you could think that something is/isn't allowed, but
once the domain is started and a port is created by the configured
network, the opposite might be true.
To solve this problem hypervisor drivers need to do an extra
validation step when the domain is being started. I recently (commit
3cff23f7, libvirt 5.7.0) added a function to peform such validation
for all interfaces to the QEMU driver -
qemuDomainValidateActualNetDef() - but while that function is a good
single point to call for the multiple places that need to "start" an
interface (domain startup, device hotplug, device update), it can't be
called by the other hypervisor drivers, since 1) it's in the QEMU
driver, and 2) it contains some checks specific to QEMU. For
validation that applies to network devices on *all* hypervisors, we
need yet another interface validation function that can be called by
any hypervisor driver (not just QEMU) right after its network port has
been created during domain startup or hotplug. This patch adds that
function - virDomainActualNetDefValidate(), in the conf directory,
and calls it in appropriate places in the QEMU, lxc, and libxl
drivers.
This new function is the place to put all network device validation
that 1) is hypervisor agnostic, and 2) can't be done until we know the
"actual type" of an interface.
There is no framework for validation at domain startup as there is for
post-parse validation, but I don't want to create a whole elaborate
system that will only be used by one type of device. For that reason,
I just made a single function that should be called directly from the
hypervisors, when they are initializing interfaces to start a domain,
right after conditionally allocating the network port (and regardless
of whether or not that was actually needed). In the case of the QEMU
driver, qemuDomainValidateActualNetDef() is already called in all the
appropriate places, so we can just call the new function from
there. In the case of the other hypervisors, we search for
virDomainNetAllocateActualDevice() (which is the hypervisor-agnostic
function that calls virNetworkPortCreateXML()), and add the call to our
new function right after that.
The new function itself could be plunked down into many places in the
code, but we already have 3 validation functions for network devices
in 2 different places (not counting any basic validation done in
virDomainNetDefParseXML() itself):
1) post-parse hypervisor-agnostic
(virDomainNetDefValidate() - domain_conf.c:6145)
2) post-parse hypervisor-specific
(qemuDomainDeviceDefValidateNetwork() - qemu_domain.c:5498)
3) domain-start hypervisor-specific
(qemuDomainValidateActualNetDef() - qemu_domain.c:5390)
I placed (3) right next to (2) when I added it, specifically to avoid
spreading validation all over the code. For the same reason, I decided
to put this new function right next to (1) - this way if someone needs
to add validation specific to qemu, they go to one location, and if
they need to add validation applying to everyone, they go to the
other. It looks a bit strange to have a public function in between a
bunch of statics, but I think it's better than the alternative of
further fragmentation. (I'm open to other ideas though, of course.)
Signed-off-by: Laine Stump <laine@redhat.com>
Reviewed-by: Cole Robinson <crobinso@redhat.com>
2019-10-18 19:48:13 +00:00
|
|
|
/* final validation now that actual type is known */
|
|
|
|
if (virDomainActualNetDefValidate(net) < 0)
|
|
|
|
return -1;
|
|
|
|
|
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:
|
2019-12-04 08:08:17 +00:00
|
|
|
if (!(veth = virLXCProcessSetupInterfaceDirect(driver, 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) {
|
2020-02-17 17:17:23 +00:00
|
|
|
if (virNetDevSupportsBandwidth(type)) {
|
2017-10-02 12:12:44 +00:00
|
|
|
if (virNetDevBandwidthSet(net->ifname, actualBandwidth, false,
|
|
|
|
!virDomainNetTypeSharesHostView(net)) < 0)
|
2015-01-07 14:52:21 +00:00
|
|
|
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
|
|
|
|
2018-07-26 15:25:58 +00:00
|
|
|
(*veths)[i] = veth;
|
2014-06-27 08:41:22 +00:00
|
|
|
|
2019-10-20 11:49:46 +00:00
|
|
|
def->nets[i]->ifname_guest_actual = g_strdup(veth);
|
2014-12-19 09:08:38 +00:00
|
|
|
|
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) {
|
2019-10-22 13:26:14 +00:00
|
|
|
net->ifname_guest = g_strdup_printf("eth%zu", niface);
|
2014-06-27 08:41:22 +00:00
|
|
|
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) {
|
2019-04-30 14:17:14 +00:00
|
|
|
virErrorPreserveLast(&save_err);
|
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];
|
2019-10-01 17:56:35 +00:00
|
|
|
const virNetDevVPortProfile *vport = virDomainNetGetActualVirtPortProfile(iface);
|
2012-07-13 11:39:29 +00:00
|
|
|
if (vport && vport->virtPortType == VIR_NETDEV_VPORT_PROFILE_OPENVSWITCH)
|
|
|
|
ignore_value(virNetDevOpenvswitchRemovePort(
|
|
|
|
virDomainNetGetActualBridgeName(iface),
|
|
|
|
iface->ifname));
|
2018-07-26 14:32:04 +00:00
|
|
|
if (iface->type == VIR_DOMAIN_NET_TYPE_NETWORK && netconn)
|
|
|
|
virDomainNetReleaseActualDevice(netconn, def, iface);
|
2012-07-13 11:39:29 +00:00
|
|
|
}
|
2019-04-30 14:17:14 +00:00
|
|
|
virErrorRestore(&save_err);
|
2012-07-13 11:39:29 +00:00
|
|
|
}
|
2018-07-26 14:32:04 +00:00
|
|
|
virObjectUnref(netconn);
|
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++) {
|
2020-05-12 16:53:07 +00:00
|
|
|
g_free(def->nets[i]->ifname_guest_actual);
|
|
|
|
def->nets[i]->ifname_guest_actual = NULL;
|
2014-06-27 08:41:22 +00:00
|
|
|
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");
|
|
|
|
}
|
2018-04-23 14:40:48 +00:00
|
|
|
if (!vm->persistent)
|
2013-01-11 16:04:47 +00:00
|
|
|
virDomainObjListRemove(driver->domains, vm);
|
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);
|
2018-04-23 14:40:48 +00:00
|
|
|
if (!vm->persistent)
|
2013-01-11 16:04:47 +00:00
|
|
|
virDomainObjListRemove(driver->domains, vm);
|
2012-07-23 11:25:37 +00:00
|
|
|
}
|
2012-07-17 10:55:38 +00:00
|
|
|
}
|
|
|
|
|
2018-04-23 14:40:48 +00:00
|
|
|
/* NB: virLXCProcessConnectMonitor will perform the virObjectRef(vm)
|
|
|
|
* before adding monitorCallbacks. Since we are now done with the @vm
|
|
|
|
* we can Unref/Unlock */
|
|
|
|
virDomainObjEndAPI(&vm);
|
2018-06-11 19:38:17 +00:00
|
|
|
virObjectEventStateQueue(driver->domainEventState, event);
|
2012-07-17 10:55:38 +00:00
|
|
|
}
|
|
|
|
|
2019-10-14 12:45:33 +00:00
|
|
|
static void virLXCProcessMonitorExitNotify(virLXCMonitorPtr mon G_GNUC_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)
|
|
|
|
{
|
2020-05-12 16:53:07 +00:00
|
|
|
g_autofree char *path = NULL;
|
2013-03-06 14:56:49 +00:00
|
|
|
struct stat sb;
|
|
|
|
|
2019-10-22 13:26:14 +00:00
|
|
|
path = g_strdup_printf("/proc/%lld/ns/%s", (long long)pid, nsname);
|
2013-03-06 14:56:49 +00:00
|
|
|
|
|
|
|
if (stat(path, &sb) < 0) {
|
|
|
|
virReportSystemError(errno,
|
|
|
|
_("Unable to stat %s"), path);
|
2020-05-12 16:53:07 +00:00
|
|
|
return -1;
|
2013-03-06 14:56:49 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
*inode = sb.st_ino;
|
|
|
|
|
2020-05-12 16:53:07 +00:00
|
|
|
return 0;
|
2013-03-06 14:56:49 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2012-11-22 17:15:54 +00:00
|
|
|
/* XXX a little evil */
|
|
|
|
extern virLXCDriverPtr lxc_driver;
|
2019-10-14 12:45:33 +00:00
|
|
|
static void virLXCProcessMonitorInitNotify(virLXCMonitorPtr mon G_GNUC_UNUSED,
|
2012-11-20 17:49:25 +00:00
|
|
|
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",
|
2018-04-25 12:42:34 +00:00
|
|
|
(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
|
|
|
|
2019-11-27 12:53:10 +00:00
|
|
|
if (virDomainObjSave(vm, lxc_driver->xmlopt, cfg->stateDir) < 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
|
2018-04-23 14:40:48 +00:00
|
|
|
* deleted while the monitor is active. This will be unreffed
|
|
|
|
* during EOFNotify processing. */
|
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;
|
|
|
|
|
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
|
2020-07-09 04:42:21 +00:00
|
|
|
* here because the domain has already been suspended using the
|
2014-02-14 15:42:48 +00:00
|
|
|
* 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
|
|
|
}
|
2017-07-21 13:56:46 +00:00
|
|
|
} else if (vm->pid > 0) {
|
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,
|
|
|
|
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;
|
2020-05-12 16:53:07 +00:00
|
|
|
g_autofree char *filterstr = NULL;
|
|
|
|
g_autofree char *outputstr = NULL;
|
2012-07-13 11:39:29 +00:00
|
|
|
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. */
|
2019-08-01 12:52:00 +00:00
|
|
|
virCommandAddEnvPass(cmd, "PATH");
|
2012-07-13 11:39:29 +00:00
|
|
|
|
|
|
|
virCommandAddEnvFormat(cmd, "LIBVIRT_DEBUG=%d",
|
|
|
|
virLogGetDefaultPriority());
|
|
|
|
|
|
|
|
if (virLogGetNbFilters() > 0) {
|
|
|
|
filterstr = virLogGetFilters();
|
|
|
|
if (!filterstr) {
|
|
|
|
virReportOOMError();
|
2018-07-25 12:48:13 +00:00
|
|
|
goto error;
|
2012-07-13 11:39:29 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
virCommandAddEnvPair(cmd, "LIBVIRT_LOG_FILTERS", 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();
|
2018-07-25 12:48:13 +00:00
|
|
|
goto error;
|
2012-07-13 11:39:29 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
virCommandAddEnvPair(cmd, "LIBVIRT_LOG_OUTPUTS", 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) {
|
2020-05-12 16:53:07 +00:00
|
|
|
g_autofree char *tmp = g_strdup_printf("--share-%s",
|
|
|
|
nsInfoLocal[i]);
|
2015-08-20 13:46:17 +00:00
|
|
|
virCommandAddArg(cmd, tmp);
|
|
|
|
virCommandAddArgFormat(cmd, "%d", nsInheritFDs[i]);
|
|
|
|
virCommandPassFD(cmd, nsInheritFDs[i], 0);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2012-07-13 11:39:29 +00:00
|
|
|
virCommandAddArgPair(cmd, "--security",
|
|
|
|
virSecurityManagerGetModel(driver->securityManager));
|
|
|
|
|
|
|
|
virCommandAddArg(cmd, "--handshake");
|
|
|
|
virCommandAddArgFormat(cmd, "%d", handshakefd);
|
|
|
|
|
2018-07-26 15:25:58 +00:00
|
|
|
for (i = 0; veths && veths[i]; 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
|
|
|
|
2014-03-25 06:49:26 +00:00
|
|
|
cleanup:
|
2013-07-16 15:45:05 +00:00
|
|
|
virObjectUnref(cfg);
|
2018-07-25 12:48:13 +00:00
|
|
|
return cmd;
|
|
|
|
error:
|
|
|
|
virCommandFree(cmd);
|
|
|
|
cmd = NULL;
|
|
|
|
goto cleanup;
|
2012-07-13 11:39:29 +00:00
|
|
|
}
|
|
|
|
|
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;
|
|
|
|
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"));
|
2019-10-21 18:18:55 +00:00
|
|
|
return -1;
|
2012-07-13 11:39:29 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
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);
|
2019-10-21 18:18:55 +00:00
|
|
|
return -1;
|
2013-03-04 20:47:37 +00:00
|
|
|
}
|
|
|
|
|
2019-10-21 18:18:55 +00:00
|
|
|
if (isdead)
|
|
|
|
return got;
|
2012-07-13 11:39:29 +00:00
|
|
|
|
2019-10-02 17:01:11 +00:00
|
|
|
g_usleep(100*1000);
|
2012-07-13 11:39:29 +00:00
|
|
|
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
|
|
|
|
2019-10-21 18:18:55 +00:00
|
|
|
return -1;
|
2013-03-04 20:47:37 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
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;
|
|
|
|
|
2019-12-10 12:51:54 +00:00
|
|
|
if (!(root = virDomainFSDefNew(NULL)))
|
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;
|
|
|
|
|
2019-10-20 11:49:46 +00:00
|
|
|
root->src->path = g_strdup("/");
|
|
|
|
root->dst = g_strdup("/");
|
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;
|
2020-05-12 16:53:07 +00:00
|
|
|
g_autofree int *ttyFDs = NULL;
|
2012-07-13 11:39:29 +00:00
|
|
|
size_t i;
|
2020-05-12 16:53:07 +00:00
|
|
|
g_autofree char *logfile = NULL;
|
2012-07-13 11:39:29 +00:00
|
|
|
int logfd = -1;
|
2019-02-22 14:01:02 +00:00
|
|
|
VIR_AUTOSTRINGLIST veths = NULL;
|
2012-07-13 11:39:29 +00:00
|
|
|
int handshakefds[2] = { -1, -1 };
|
|
|
|
off_t pos = -1;
|
|
|
|
char ebuf[1024];
|
2020-05-12 16:53:07 +00:00
|
|
|
g_autofree char *timestamp = NULL;
|
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;
|
2020-05-12 16:53:07 +00:00
|
|
|
g_autofree char *pidfile = NULL;
|
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)) {
|
2018-07-30 09:06:31 +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)) {
|
2018-07-30 09:06:31 +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)) {
|
2018-07-30 09:06:31 +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;
|
|
|
|
}
|
2018-07-30 09:06:31 +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) {
|
2020-05-12 17:31:16 +00:00
|
|
|
virDomainResourceDefPtr res = g_new0(virDomainResourceDef, 1);
|
2013-07-26 14:59:16 +00:00
|
|
|
|
2019-10-20 11:49:46 +00:00
|
|
|
res->partition = g_strdup("/machine");
|
2013-07-26 14:59:16 +00:00
|
|
|
|
|
|
|
vm->def->resource = res;
|
|
|
|
}
|
|
|
|
|
2019-10-22 13:26:14 +00:00
|
|
|
logfile = g_strdup_printf("%s/%s.log", cfg->logDir, vm->def->name);
|
2015-01-16 11:54:30 +00:00
|
|
|
|
|
|
|
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");
|
2019-11-27 12:41:59 +00:00
|
|
|
if (virDomainObjSetDefTransient(driver->xmlopt, vm, NULL) < 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)) {
|
2020-05-12 16:53:07 +00:00
|
|
|
g_autofree char *xml = virDomainDefFormat(vm->def, driver->xmlopt, 0);
|
2012-07-13 11:39:29 +00:00
|
|
|
|
|
|
|
/*
|
|
|
|
* If the script raised an error abort the launch
|
|
|
|
*/
|
2020-05-12 16:53:07 +00:00
|
|
|
if (virHookCall(VIR_HOOK_DRIVER_LXC, vm->def->name,
|
|
|
|
VIR_HOOK_LXC_OP_PREPARE, VIR_HOOK_SUBOP_BEGIN,
|
|
|
|
NULL, xml, NULL) < 0)
|
2012-07-13 11:39:29 +00:00
|
|
|
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;
|
2020-05-12 17:31:16 +00:00
|
|
|
ttyFDs = g_new0(int, nttyFDs);
|
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
|
|
|
|
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,
|
2019-09-11 05:53:09 +00:00
|
|
|
vm->def, NULL, false, 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;
|
|
|
|
}
|
|
|
|
|
2020-05-12 16:53:07 +00:00
|
|
|
g_free(vm->def->consoles[i]->source->data.file.path);
|
2016-10-21 11:45:54 +00:00
|
|
|
vm->def->consoles[i]->source->data.file.path = ttyPath;
|
2012-07-13 11:39:29 +00:00
|
|
|
|
2020-05-12 16:53:07 +00:00
|
|
|
g_free(vm->def->consoles[i]->info.alias);
|
2019-10-22 13:26:14 +00:00
|
|
|
vm->def->consoles[i]->info.alias = g_strdup_printf("console%zu", i);
|
2012-07-13 11:39:29 +00:00
|
|
|
}
|
|
|
|
|
2015-02-12 19:32:39 +00:00
|
|
|
VIR_DEBUG("Setting up Interfaces");
|
2019-12-04 08:08:17 +00:00
|
|
|
if (virLXCProcessSetupInterfaces(driver, vm->def, &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");
|
2019-12-04 08:08:17 +00:00
|
|
|
if (virLXCProcessSetupNamespaces(driver, vm->def->namespaceData, nsInheritFDs) < 0)
|
2015-08-20 13:46:17 +00:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2020-01-24 15:22:12 +00:00
|
|
|
if (virPipe(handshakefds) < 0)
|
2012-07-13 11:39:29 +00:00
|
|
|
goto cleanup;
|
|
|
|
|
2012-07-13 11:49:24 +00:00
|
|
|
if (!(cmd = virLXCProcessBuildControllerCmd(driver,
|
|
|
|
vm,
|
2018-07-26 15:25:58 +00:00
|
|
|
veths,
|
2012-07-13 11:49:24 +00:00
|
|
|
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)) {
|
2020-05-12 16:53:07 +00:00
|
|
|
g_autofree char *xml = virDomainDefFormat(vm->def, driver->xmlopt, 0);
|
2012-07-13 11:39:29 +00:00
|
|
|
|
|
|
|
/*
|
|
|
|
* If the script raised an error abort the launch
|
|
|
|
*/
|
2020-05-12 16:53:07 +00:00
|
|
|
if (virHookCall(VIR_HOOK_DRIVER_LXC, vm->def->name,
|
|
|
|
VIR_HOOK_LXC_OP_START, VIR_HOOK_SUBOP_BEGIN,
|
|
|
|
NULL, xml, NULL) < 0)
|
2012-07-13 11:39:29 +00:00
|
|
|
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",
|
2020-02-26 17:57:34 +00:00
|
|
|
g_strerror(errno));
|
2012-07-13 11:39:29 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/* 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",
|
2020-02-26 17:57:34 +00:00
|
|
|
g_strerror(errno));
|
2012-07-13 11:39:29 +00:00
|
|
|
|
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))
|
2019-11-13 13:53:42 +00:00
|
|
|
g_snprintf(ebuf, sizeof(ebuf), _("unexpected exit status %d"),
|
|
|
|
WEXITSTATUS(status));
|
2014-02-20 00:32:19 +00:00
|
|
|
else
|
2019-11-13 13:53:42 +00:00
|
|
|
g_snprintf(ebuf, sizeof(ebuf), "%s", _("terminated abnormally"));
|
2014-02-20 00:32:19 +00:00
|
|
|
}
|
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;
|
|
|
|
}
|
|
|
|
|
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 */
|
2019-11-27 12:53:10 +00:00
|
|
|
if (virDomainObjSave(vm, driver->xmlopt, cfg->stateDir) < 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
|
|
|
|
2020-01-31 16:04:24 +00:00
|
|
|
if (g_atomic_int_add(&driver->nactive, 1) == 0 && 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
|
|
|
}
|
|
|
|
|
2017-07-21 13:51:03 +00:00
|
|
|
priv->machineName = virLXCDomainGetMachineName(vm->def, vm->pid);
|
|
|
|
if (!priv->machineName)
|
|
|
|
goto cleanup;
|
|
|
|
|
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",
|
2017-07-21 13:51:03 +00:00
|
|
|
vm->pid, -1, priv->machineName,
|
|
|
|
&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
|
|
|
}
|
|
|
|
|
|
|
|
/* 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)) {
|
2020-05-12 16:53:07 +00:00
|
|
|
g_autofree char *xml = virDomainDefFormat(vm->def, driver->xmlopt, 0);
|
2012-07-13 11:39:29 +00:00
|
|
|
|
|
|
|
/*
|
|
|
|
* If the script raised an error abort the launch
|
|
|
|
*/
|
2020-05-12 16:53:07 +00:00
|
|
|
if (virHookCall(VIR_HOOK_DRIVER_LXC, vm->def->name,
|
|
|
|
VIR_HOOK_LXC_OP_STARTED, VIR_HOOK_SUBOP_BEGIN,
|
|
|
|
NULL, xml, NULL) < 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) {
|
2018-12-06 17:32:37 +00:00
|
|
|
virErrorPreserveLast(&err);
|
2017-07-21 13:56:46 +00:00
|
|
|
virLXCProcessStop(driver, vm, VIR_DOMAIN_SHUTOFF_FAILED);
|
2012-07-13 11:39:29 +00:00
|
|
|
}
|
2015-02-04 13:42:56 +00:00
|
|
|
virCommandFree(cmd);
|
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_FORCE_CLOSE(handshakefds[0]);
|
|
|
|
VIR_FORCE_CLOSE(handshakefds[1]);
|
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
|
|
|
|
2018-12-06 17:32:37 +00:00
|
|
|
virErrorRestore(&err);
|
2012-07-13 11:39:29 +00:00
|
|
|
|
|
|
|
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);
|
2018-06-11 19:38:17 +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
|
|
|
|
*/
|
2018-03-27 13:32:07 +00:00
|
|
|
virConnectPtr conn = virConnectOpen("lxc:///system");
|
2012-07-13 11:39:29 +00:00
|
|
|
/* 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
|
|
|
|
2019-09-06 11:59:59 +00:00
|
|
|
virDomainObjListForEach(driver->domains, false,
|
2013-01-11 13:54:15 +00:00
|
|
|
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
|
|
|
}
|
|
|
|
|
2019-02-01 17:11:22 +00:00
|
|
|
|
|
|
|
static void
|
|
|
|
virLXCProcessReconnectNotifyNets(virDomainDefPtr def)
|
|
|
|
{
|
|
|
|
size_t i;
|
|
|
|
virConnectPtr conn = NULL;
|
|
|
|
|
|
|
|
for (i = 0; i < def->nnets; i++) {
|
|
|
|
virDomainNetDefPtr net = def->nets[i];
|
|
|
|
/* keep others from trying to use the macvtap device name, but
|
|
|
|
* don't return error if this happens, since that causes the
|
|
|
|
* domain to be unceremoniously killed, which would be *very*
|
|
|
|
* impolite.
|
|
|
|
*/
|
|
|
|
if (virDomainNetGetActualType(net) == VIR_DOMAIN_NET_TYPE_DIRECT)
|
|
|
|
ignore_value(virNetDevMacVLanReserveName(net->ifname, false));
|
|
|
|
|
|
|
|
if (net->type == VIR_DOMAIN_NET_TYPE_NETWORK) {
|
|
|
|
if (!conn && !(conn = virGetConnectNetwork()))
|
|
|
|
continue;
|
|
|
|
virDomainNetNotifyActualDevice(conn, def, net);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
virObjectUnref(conn);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
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;
|
2019-02-01 17:40:41 +00:00
|
|
|
virLXCDriverConfigPtr cfg = virLXCDriverGetConfig(driver);
|
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);
|
|
|
|
|
2020-01-31 16:04:24 +00:00
|
|
|
if (g_atomic_int_add(&driver->nactive, 1) == 0 && 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;
|
|
|
|
|
2017-07-21 13:51:03 +00:00
|
|
|
priv->machineName = virLXCDomainGetMachineName(vm->def, vm->pid);
|
|
|
|
if (!priv->machineName)
|
|
|
|
goto cleanup;
|
|
|
|
|
|
|
|
if (virCgroupNewDetectMachine(vm->def->name, "lxc", vm->pid, -1,
|
|
|
|
priv->machineName, &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;
|
|
|
|
}
|
|
|
|
|
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;
|
|
|
|
|
2019-02-01 17:11:22 +00:00
|
|
|
virLXCProcessReconnectNotifyNets(vm->def);
|
|
|
|
|
2019-11-27 12:53:10 +00:00
|
|
|
if (virDomainObjSave(vm, driver->xmlopt, cfg->stateDir) < 0)
|
2019-02-01 17:40:41 +00:00
|
|
|
VIR_WARN("Cannot update XML for running LXC guest %s", vm->def->name);
|
|
|
|
|
2012-07-13 11:39:29 +00:00
|
|
|
/* now that we know it's reconnected call the hook if present */
|
|
|
|
if (virHookPresent(VIR_HOOK_DRIVER_LXC)) {
|
2020-05-12 16:53:07 +00:00
|
|
|
g_autofree char *xml = virDomainDefFormat(vm->def, driver->xmlopt, 0);
|
2012-07-13 11:39:29 +00:00
|
|
|
|
|
|
|
/* we can't stop the operation even if the script raised an error */
|
2020-05-12 16:53:07 +00:00
|
|
|
if (virHookCall(VIR_HOOK_DRIVER_LXC, vm->def->name,
|
|
|
|
VIR_HOOK_LXC_OP_RECONNECT, VIR_HOOK_SUBOP_BEGIN,
|
|
|
|
NULL, xml, NULL) < 0)
|
2012-07-13 11:39:29 +00:00
|
|
|
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:
|
2019-02-01 17:40:41 +00:00
|
|
|
virObjectUnref(cfg);
|
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
|
|
|
{
|
2019-09-06 11:59:59 +00:00
|
|
|
virDomainObjListForEach(doms, false, virLXCProcessReconnectDomain, driver);
|
2012-07-13 11:39:29 +00:00
|
|
|
return 0;
|
|
|
|
}
|