2012-07-13 12:39:29 +01:00
|
|
|
/*
|
2016-05-04 13:18:16 -04:00
|
|
|
* Copyright (C) 2010-2016 Red Hat, Inc.
|
2012-07-13 12:39:29 +01: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 16:30:55 -06:00
|
|
|
* License along with this library. If not, see
|
2012-07-21 18:06:23 +08:00
|
|
|
* <http://www.gnu.org/licenses/>.
|
2012-07-13 12:39:29 +01:00
|
|
|
*/
|
|
|
|
|
|
|
|
#include <config.h>
|
|
|
|
|
2013-03-08 17:28:37 +01:00
|
|
|
#include <sys/stat.h>
|
2012-07-13 12:39:29 +01: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 17:39:04 +08:00
|
|
|
#include "lxc_fuse.h"
|
2012-07-13 12:39:29 +01:00
|
|
|
#include "datatypes.h"
|
|
|
|
#include "virfile.h"
|
|
|
|
#include "virpidfile.h"
|
|
|
|
#include "virnetdev.h"
|
|
|
|
#include "virnetdevveth.h"
|
|
|
|
#include "virnetdevbridge.h"
|
2015-03-17 13:46:44 -04:00
|
|
|
#include "virnetdevopenvswitch.h"
|
2012-07-13 12:39:29 +01:00
|
|
|
#include "virtime.h"
|
|
|
|
#include "domain_nwfilter.h"
|
2012-12-12 18:06:53 +00:00
|
|
|
#include "viralloc.h"
|
2012-07-13 12:39:29 +01:00
|
|
|
#include "domain_audit.h"
|
2020-12-10 21:38:07 -03:00
|
|
|
#include "domain_validate.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 12:36:23 +02:00
|
|
|
#include "virstring.h"
|
2013-07-22 16:36:06 +01:00
|
|
|
#include "virprocess.h"
|
2013-09-30 16:46:29 +02:00
|
|
|
#include "virsystemd.h"
|
2014-11-18 15:55:48 -08:00
|
|
|
#include "netdev_bandwidth_conf.h"
|
2020-02-16 22:59:28 +01:00
|
|
|
#include "virutil.h"
|
2012-07-13 12:39:29 +01: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 12:39:29 +01:00
|
|
|
#define START_POSTFIX ": starting up\n"
|
|
|
|
|
2018-03-27 11:39:53 -04:00
|
|
|
static void
|
2021-03-11 08:16:13 +01:00
|
|
|
lxcProcessAutoDestroy(virDomainObj *dom,
|
2013-07-15 19:08:11 +02:00
|
|
|
virConnectPtr conn,
|
|
|
|
void *opaque)
|
2012-07-13 12:39:29 +01:00
|
|
|
{
|
2021-03-11 08:16:13 +01:00
|
|
|
virLXCDriver *driver = opaque;
|
|
|
|
virObjectEvent *event = NULL;
|
|
|
|
virLXCDomainObjPrivate *priv;
|
2012-07-13 12:39:29 +01:00
|
|
|
|
2013-07-15 19:08:11 +02:00
|
|
|
VIR_DEBUG("driver=%p dom=%s conn=%p", driver, dom->def->name, conn);
|
2012-07-13 12:39:29 +01:00
|
|
|
|
2012-07-17 11:55:38 +01:00
|
|
|
priv = dom->privateData;
|
2012-07-13 12:39:29 +01:00
|
|
|
VIR_DEBUG("Killing domain");
|
2013-07-15 19:08:11 +02:00
|
|
|
virLXCProcessStop(driver, dom, VIR_DOMAIN_SHUTOFF_DESTROYED);
|
2012-07-13 12:39:29 +01:00
|
|
|
virDomainAuditStop(dom, "destroyed");
|
2013-11-21 18:03:26 +01:00
|
|
|
event = virDomainEventLifecycleNewFromObj(dom,
|
2012-07-13 12:39:29 +01:00
|
|
|
VIR_DOMAIN_EVENT_STOPPED,
|
|
|
|
VIR_DOMAIN_EVENT_STOPPED_DESTROYED);
|
2012-07-17 11:55:38 +01:00
|
|
|
priv->doneStopEvent = true;
|
2012-07-13 12:39:29 +01:00
|
|
|
|
2018-03-27 11:39:53 -04:00
|
|
|
if (!dom->persistent)
|
2013-07-15 19:08:11 +02:00
|
|
|
virDomainObjListRemove(driver->domains, dom);
|
2012-07-13 12:39:29 +01:00
|
|
|
|
2018-06-11 15:38:17 -04:00
|
|
|
virObjectEventStateQueue(driver->domainEventState, event);
|
2012-07-13 12:39:29 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Precondition: driver is locked
|
|
|
|
*/
|
2012-07-23 12:25:37 +01:00
|
|
|
static int
|
2021-03-11 08:16:13 +01:00
|
|
|
virLXCProcessReboot(virLXCDriver *driver,
|
|
|
|
virDomainObj *vm)
|
2012-07-23 12:25:37 +01:00
|
|
|
{
|
2013-07-15 19:08:11 +02:00
|
|
|
virConnectPtr conn = virCloseCallbacksGetConn(driver->closeCallbacks, vm);
|
2012-07-23 12:25:37 +01:00
|
|
|
int reason = vm->state.reason;
|
|
|
|
bool autodestroy = false;
|
|
|
|
int ret = -1;
|
2021-03-11 08:16:13 +01:00
|
|
|
virDomainDef *savedDef;
|
2012-07-23 12:25:37 +01:00
|
|
|
|
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 15:08:20 +01:00
|
|
|
VIR_DEBUG("Faking reboot");
|
|
|
|
|
2012-07-23 12:25:37 +01:00
|
|
|
if (conn) {
|
2014-01-03 08:08:52 -07:00
|
|
|
virObjectRef(conn);
|
2012-07-23 12:25:37 +01:00
|
|
|
autodestroy = true;
|
|
|
|
} else {
|
2018-03-27 14:32:07 +01:00
|
|
|
conn = virConnectOpen("lxc:///system");
|
2012-07-23 12:25:37 +01: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
|
|
|
|
*/
|
2021-03-24 10:32:58 +01:00
|
|
|
savedDef = g_steal_pointer(&vm->newDef);
|
2012-07-23 12:25:37 +01:00
|
|
|
virLXCProcessStop(driver, vm, VIR_DOMAIN_SHUTOFF_SHUTDOWN);
|
|
|
|
vm->newDef = savedDef;
|
2013-07-09 18:15:45 +01:00
|
|
|
if (virLXCProcessStart(conn, driver, vm,
|
|
|
|
0, NULL, autodestroy, reason) < 0) {
|
2012-07-23 12:25:37 +01:00
|
|
|
VIR_WARN("Unable to handle reboot of vm %s",
|
|
|
|
vm->def->name);
|
|
|
|
goto cleanup;
|
|
|
|
}
|
|
|
|
|
|
|
|
ret = 0;
|
|
|
|
|
2014-03-25 07:49:26 +01:00
|
|
|
cleanup:
|
2014-01-03 08:08:52 -07:00
|
|
|
virObjectUnref(conn);
|
2012-07-23 12:25:37 +01:00
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2012-07-13 12:39:29 +01:00
|
|
|
|
2015-07-28 18:25:59 +02:00
|
|
|
static void
|
2021-03-11 08:16:13 +01:00
|
|
|
lxcProcessRemoveDomainStatus(virLXCDriverConfig *cfg,
|
|
|
|
virDomainObj *vm)
|
2015-07-28 18:25:59 +02:00
|
|
|
{
|
2020-05-12 17:53:07 +01:00
|
|
|
g_autofree char *file = g_strdup_printf("%s/%s.xml",
|
|
|
|
cfg->stateDir,
|
|
|
|
vm->def->name);
|
2015-07-28 18:25:59 +02:00
|
|
|
|
|
|
|
if (unlink(file) < 0 && errno != ENOENT && errno != ENOTDIR)
|
|
|
|
VIR_WARN("Failed to remove domain XML for %s: %s",
|
2020-02-26 18:57:34 +01:00
|
|
|
vm->def->name, g_strerror(errno));
|
2015-07-28 18:25:59 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2020-11-11 13:51:21 +01:00
|
|
|
typedef enum {
|
|
|
|
VIR_LXC_PROCESS_CLEANUP_RELEASE_SECLABEL = (1 << 0),
|
|
|
|
VIR_LXC_PROCESS_CLEANUP_RESTORE_SECLABEL = (1 << 1),
|
|
|
|
VIR_LXC_PROCESS_CLEANUP_REMOVE_TRANSIENT = (1 << 2),
|
|
|
|
} virLXCProcessCleanupFlags;
|
|
|
|
|
2012-07-13 12:39:29 +01:00
|
|
|
/**
|
2012-07-13 12:49:24 +01:00
|
|
|
* virLXCProcessCleanup:
|
2012-07-13 12:39:29 +01:00
|
|
|
* @driver: pointer to driver structure
|
|
|
|
* @vm: pointer to VM to clean up
|
|
|
|
* @reason: reason for switching the VM to shutoff state
|
2020-11-11 13:51:21 +01:00
|
|
|
* @flags: allows to run selective cleanups only
|
2012-07-13 12:39:29 +01:00
|
|
|
*
|
2020-11-11 13:51:21 +01:00
|
|
|
* Clean out resources associated with the now dead VM.
|
|
|
|
* If @flags is zero then whole cleanup process is done,
|
|
|
|
* otherwise only selected sections are run.
|
2012-07-13 12:39:29 +01:00
|
|
|
*/
|
2021-03-11 08:16:13 +01:00
|
|
|
static void virLXCProcessCleanup(virLXCDriver *driver,
|
|
|
|
virDomainObj *vm,
|
2020-11-11 13:51:21 +01:00
|
|
|
virDomainShutoffReason reason,
|
|
|
|
unsigned int flags)
|
2012-07-13 12:39:29 +01: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 15:09:33 +01:00
|
|
|
size_t i;
|
2021-03-11 08:16:13 +01:00
|
|
|
virLXCDomainObjPrivate *priv = vm->privateData;
|
2019-10-01 13:56:35 -04:00
|
|
|
const virNetDevVPortProfile *vport = NULL;
|
2021-12-10 14:56:46 +01:00
|
|
|
g_autoptr(virLXCDriverConfig) cfg = virLXCDriverGetConfig(driver);
|
2021-01-08 00:31:05 -05:00
|
|
|
g_autoptr(virConnect) conn = NULL;
|
2012-07-13 12:39:29 +01:00
|
|
|
|
2020-11-11 13:51:21 +01:00
|
|
|
VIR_DEBUG("Cleanup VM name=%s pid=%d reason=%d flags=0x%x",
|
|
|
|
vm->def->name, (int)vm->pid, (int)reason, flags);
|
|
|
|
|
|
|
|
if (flags == 0)
|
|
|
|
flags = ~0;
|
2012-07-17 15:54:08 +01:00
|
|
|
|
2012-07-13 12:39:29 +01:00
|
|
|
/* now that we know it's stopped call the hook if present */
|
|
|
|
if (virHookPresent(VIR_HOOK_DRIVER_LXC)) {
|
2020-05-12 17:53:07 +01:00
|
|
|
g_autofree char *xml = virDomainDefFormat(vm->def, driver->xmlopt, 0);
|
2012-07-13 12:39:29 +01: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);
|
|
|
|
}
|
|
|
|
|
2020-11-11 13:51:21 +01:00
|
|
|
if (flags & VIR_LXC_PROCESS_CLEANUP_RESTORE_SECLABEL) {
|
|
|
|
virSecurityManagerRestoreAllLabel(driver->securityManager,
|
|
|
|
vm->def, false, false);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (flags & VIR_LXC_PROCESS_CLEANUP_RELEASE_SECLABEL) {
|
|
|
|
virSecurityManagerReleaseLabel(driver->securityManager, vm->def);
|
|
|
|
}
|
|
|
|
|
2019-01-24 17:38:10 +01:00
|
|
|
/* Clear out dynamically assigned labels */
|
|
|
|
if (vm->def->nseclabels &&
|
|
|
|
vm->def->seclabels[0]->type == VIR_DOMAIN_SECLABEL_DYNAMIC) {
|
2022-01-28 18:42:45 +01:00
|
|
|
g_clear_pointer(&vm->def->seclabels[0]->model, g_free);
|
|
|
|
g_clear_pointer(&vm->def->seclabels[0]->label, g_free);
|
|
|
|
g_clear_pointer(&vm->def->seclabels[0]->imagelabel, g_free);
|
2019-01-24 17:38:10 +01:00
|
|
|
}
|
|
|
|
|
2012-07-13 12:39:29 +01:00
|
|
|
/* Stop autodestroy in case guest is restarted */
|
2013-07-15 19:08:11 +02:00
|
|
|
virCloseCallbacksUnset(driver->closeCallbacks, vm,
|
|
|
|
lxcProcessAutoDestroy);
|
2012-07-13 12:39:29 +01:00
|
|
|
|
2012-07-17 12:14:35 +01:00
|
|
|
if (priv->monitor) {
|
|
|
|
virLXCMonitorClose(priv->monitor);
|
2022-01-28 18:42:45 +01:00
|
|
|
g_clear_pointer(&priv->monitor, virObjectUnref);
|
2012-07-17 12:14:35 +01:00
|
|
|
}
|
2012-07-13 12:39:29 +01:00
|
|
|
|
2013-07-16 17:45:05 +02:00
|
|
|
virPidFileDelete(cfg->stateDir, vm->def->name);
|
2015-07-28 18:25:59 +02:00
|
|
|
lxcProcessRemoveDomainStatus(cfg, vm);
|
2012-07-13 12:39:29 +01:00
|
|
|
|
|
|
|
virDomainObjSetState(vm, VIR_DOMAIN_SHUTOFF, reason);
|
|
|
|
vm->pid = -1;
|
|
|
|
vm->def->id = -1;
|
|
|
|
|
2020-01-31 17:12:11 +01: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 16:03:33 +08:00
|
|
|
for (i = 0; i < vm->def->nnets; i++) {
|
2021-03-11 08:16:13 +01:00
|
|
|
virDomainNetDef *iface = vm->def->nets[i];
|
2012-07-13 12:39:29 +01:00
|
|
|
vport = virDomainNetGetActualVirtPortProfile(iface);
|
2013-01-22 17:09:26 -05: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 15:32:04 +01: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 12:39:29 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
virDomainConfVMNWFilterTeardown(vm);
|
|
|
|
|
2013-03-21 14:40:29 +00:00
|
|
|
if (priv->cgroup) {
|
|
|
|
virCgroupRemove(priv->cgroup);
|
2022-01-28 18:42:45 +01:00
|
|
|
g_clear_pointer(&priv->cgroup, virCgroupFree);
|
2012-07-13 12:39:29 +01:00
|
|
|
}
|
|
|
|
|
2013-09-30 16:46:29 +02: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 16:50:54 +01:00
|
|
|
virCgroupTerminateMachine(priv->machineName);
|
2022-01-28 18:42:45 +01:00
|
|
|
g_clear_pointer(&priv->machineName, g_free);
|
2013-09-30 16:46:29 +02:00
|
|
|
|
2013-10-14 14:22:17 +08:00
|
|
|
/* The "release" hook cleans up additional resources */
|
2012-07-13 12:39:29 +01:00
|
|
|
if (virHookPresent(VIR_HOOK_DRIVER_LXC)) {
|
2020-05-12 17:53:07 +01:00
|
|
|
g_autofree char *xml = virDomainDefFormat(vm->def, driver->xmlopt, 0);
|
2012-07-13 12:39:29 +01: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);
|
|
|
|
}
|
|
|
|
|
2020-11-11 13:51:21 +01:00
|
|
|
if (flags & VIR_LXC_PROCESS_CLEANUP_REMOVE_TRANSIENT)
|
|
|
|
virDomainObjRemoveTransientDef(vm);
|
2012-07-13 12:39:29 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2016-05-13 13:20:54 -04:00
|
|
|
int
|
2021-03-11 08:16:13 +01:00
|
|
|
virLXCProcessValidateInterface(virDomainNetDef *net)
|
2016-05-13 13:20:54 -04:00
|
|
|
{
|
|
|
|
if (net->script) {
|
|
|
|
virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
|
|
|
|
_("scripts are not supported on LXC network interfaces"));
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
char *
|
2021-03-11 08:16:13 +01:00
|
|
|
virLXCProcessSetupInterfaceTap(virDomainDef *vm,
|
|
|
|
virDomainNetDef *net,
|
2016-05-13 13:20:54 -04:00
|
|
|
const char *brname)
|
2012-07-13 12:39:29 +01:00
|
|
|
{
|
lxc: eliminate leaked and dangling pointers in virLXCProcessSetupInterfaceTap
The two scenarios were found by Coverity after a seemingly-unrelated
change to virLXCProcessSetupInterfaceTap() (in commit ecfc2d5f43), and
explained by John Ferlan here:
https://www.redhat.com/archives/libvir-list/2020-December/msg00810.html
To re-explain:
a) On entry to virLXCProcessSetupInterfaceTap() if net->ifname != NULL
then a copy of net->ifname is made into parentVeth, and a reference
to *that* pointer is sent down to virNetDevVethCreate().
b) If parentVeth (aka net->ifname) is a template name (e.g. "blah%d"),
then virNetDevVethCreate() calls virNetDevGenerateName(), and if
virNetDevGenerateName() successfully generates a usable name
(e.g. "blah27") then it will free the original template string
(which is pointed to by net->ifname and by parentVeth), then
replace the pointer in parentVeth with a pointer to the new
string. Note that net->ifname still points to the now-freed
template string.
c) returning back up to virLXCProcessSetupInterfaceTap(), we check if
net->ifname == NULL - it *isn't* (still contains stale pointer to
template string), so we don't replace it with the pointer to the new
string that is in parentVeth.
d) Result: the new string is leaked once we return from
virLXCProcessSetupInterfaceTap(), while there is a dangling pointer
to the old string in net->ifname.
There is also a leak if there is a failure somewhere between steps (b)
and (c) above - the failure cleanup in virNetDevVethCreate() will only
free the newly-generated parentVeth string if the original pointer was
NULL (narrator: "It wasn't."). But it's a new string allocated by
virNetDevGenerateName(), not the original string from net->ifname, so
it really does need to be freed.
The solution is to make a copy of the entire original string into a
g_autofree pointer, then iff everything is successful we g_free() the
original net->ifname and replace it by stealing the string returned by
virNetDevVethCreate().
Signed-off-by: Laine Stump <laine@redhat.com>
Reviewed-by: Michal Privoznik <mprivozn@redhat.com>
2021-01-06 13:44:40 -05:00
|
|
|
g_autofree char *parentVeth = NULL;
|
2020-12-16 14:01:06 +08:00
|
|
|
g_autofree char *containerVeth = NULL;
|
2019-10-01 13:56:35 -04:00
|
|
|
const virNetDevVPortProfile *vport = virDomainNetGetActualVirtPortProfile(net);
|
2012-07-13 12:39:29 +01:00
|
|
|
|
|
|
|
VIR_DEBUG("calling vethCreate()");
|
lxc: eliminate leaked and dangling pointers in virLXCProcessSetupInterfaceTap
The two scenarios were found by Coverity after a seemingly-unrelated
change to virLXCProcessSetupInterfaceTap() (in commit ecfc2d5f43), and
explained by John Ferlan here:
https://www.redhat.com/archives/libvir-list/2020-December/msg00810.html
To re-explain:
a) On entry to virLXCProcessSetupInterfaceTap() if net->ifname != NULL
then a copy of net->ifname is made into parentVeth, and a reference
to *that* pointer is sent down to virNetDevVethCreate().
b) If parentVeth (aka net->ifname) is a template name (e.g. "blah%d"),
then virNetDevVethCreate() calls virNetDevGenerateName(), and if
virNetDevGenerateName() successfully generates a usable name
(e.g. "blah27") then it will free the original template string
(which is pointed to by net->ifname and by parentVeth), then
replace the pointer in parentVeth with a pointer to the new
string. Note that net->ifname still points to the now-freed
template string.
c) returning back up to virLXCProcessSetupInterfaceTap(), we check if
net->ifname == NULL - it *isn't* (still contains stale pointer to
template string), so we don't replace it with the pointer to the new
string that is in parentVeth.
d) Result: the new string is leaked once we return from
virLXCProcessSetupInterfaceTap(), while there is a dangling pointer
to the old string in net->ifname.
There is also a leak if there is a failure somewhere between steps (b)
and (c) above - the failure cleanup in virNetDevVethCreate() will only
free the newly-generated parentVeth string if the original pointer was
NULL (narrator: "It wasn't."). But it's a new string allocated by
virNetDevGenerateName(), not the original string from net->ifname, so
it really does need to be freed.
The solution is to make a copy of the entire original string into a
g_autofree pointer, then iff everything is successful we g_free() the
original net->ifname and replace it by stealing the string returned by
virNetDevVethCreate().
Signed-off-by: Laine Stump <laine@redhat.com>
Reviewed-by: Michal Privoznik <mprivozn@redhat.com>
2021-01-06 13:44:40 -05:00
|
|
|
parentVeth = g_strdup(net->ifname);
|
2020-12-14 09:50:35 +08:00
|
|
|
|
2012-07-13 12:39:29 +01:00
|
|
|
if (virNetDevVethCreate(&parentVeth, &containerVeth) < 0)
|
2019-10-21 15:18:55 -03:00
|
|
|
return NULL;
|
2012-07-13 12:39:29 +01:00
|
|
|
VIR_DEBUG("parentVeth: %s, containerVeth: %s", parentVeth, containerVeth);
|
|
|
|
|
|
|
|
if (virNetDevSetMAC(containerVeth, &net->mac) < 0)
|
2019-10-21 15:18:55 -03:00
|
|
|
return NULL;
|
2012-07-13 12:39:29 +01:00
|
|
|
|
2016-05-13 13:20:54 -04: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 15:18:55 -03:00
|
|
|
return NULL;
|
2016-05-13 13:20:54 -04:00
|
|
|
} else {
|
|
|
|
if (virNetDevBridgeAddPort(brname, parentVeth) < 0)
|
2019-10-21 15:18:55 -03:00
|
|
|
return NULL;
|
2020-02-13 12:57:47 -05: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 13:20:54 -04:00
|
|
|
}
|
2012-11-27 12:55:02 +00:00
|
|
|
}
|
2012-07-13 12:39:29 +01:00
|
|
|
|
|
|
|
if (virNetDevSetOnline(parentVeth, true) < 0)
|
2019-10-21 15:18:55 -03:00
|
|
|
return NULL;
|
2012-07-13 12:39:29 +01:00
|
|
|
|
2016-06-17 14:52:48 -04: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 15:18:55 -03:00
|
|
|
return NULL;
|
2016-06-17 14:52:48 -04:00
|
|
|
}
|
|
|
|
|
2012-07-13 12:39:29 +01:00
|
|
|
if (net->filter &&
|
2018-05-11 18:39:27 +01:00
|
|
|
virDomainConfNWFilterInstantiate(vm->name, vm->uuid, net, false) < 0)
|
2019-10-21 15:18:55 -03:00
|
|
|
return NULL;
|
2012-07-13 12:39:29 +01:00
|
|
|
|
lxc: eliminate leaked and dangling pointers in virLXCProcessSetupInterfaceTap
The two scenarios were found by Coverity after a seemingly-unrelated
change to virLXCProcessSetupInterfaceTap() (in commit ecfc2d5f43), and
explained by John Ferlan here:
https://www.redhat.com/archives/libvir-list/2020-December/msg00810.html
To re-explain:
a) On entry to virLXCProcessSetupInterfaceTap() if net->ifname != NULL
then a copy of net->ifname is made into parentVeth, and a reference
to *that* pointer is sent down to virNetDevVethCreate().
b) If parentVeth (aka net->ifname) is a template name (e.g. "blah%d"),
then virNetDevVethCreate() calls virNetDevGenerateName(), and if
virNetDevGenerateName() successfully generates a usable name
(e.g. "blah27") then it will free the original template string
(which is pointed to by net->ifname and by parentVeth), then
replace the pointer in parentVeth with a pointer to the new
string. Note that net->ifname still points to the now-freed
template string.
c) returning back up to virLXCProcessSetupInterfaceTap(), we check if
net->ifname == NULL - it *isn't* (still contains stale pointer to
template string), so we don't replace it with the pointer to the new
string that is in parentVeth.
d) Result: the new string is leaked once we return from
virLXCProcessSetupInterfaceTap(), while there is a dangling pointer
to the old string in net->ifname.
There is also a leak if there is a failure somewhere between steps (b)
and (c) above - the failure cleanup in virNetDevVethCreate() will only
free the newly-generated parentVeth string if the original pointer was
NULL (narrator: "It wasn't."). But it's a new string allocated by
virNetDevGenerateName(), not the original string from net->ifname, so
it really does need to be freed.
The solution is to make a copy of the entire original string into a
g_autofree pointer, then iff everything is successful we g_free() the
original net->ifname and replace it by stealing the string returned by
virNetDevVethCreate().
Signed-off-by: Laine Stump <laine@redhat.com>
Reviewed-by: Michal Privoznik <mprivozn@redhat.com>
2021-01-06 13:44:40 -05:00
|
|
|
/* success is guaranteed, so update the interface object */
|
|
|
|
g_free(net->ifname);
|
|
|
|
net->ifname = g_steal_pointer(&parentVeth);
|
|
|
|
|
2020-12-16 14:01:06 +08:00
|
|
|
return g_steal_pointer(&containerVeth);
|
2012-07-13 12:39:29 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2019-12-04 09:08:17 +01:00
|
|
|
char *
|
2021-03-11 08:16:13 +01:00
|
|
|
virLXCProcessSetupInterfaceDirect(virLXCDriver *driver,
|
|
|
|
virDomainDef *def,
|
|
|
|
virDomainNetDef *net)
|
2012-07-13 12:39:29 +01:00
|
|
|
{
|
|
|
|
char *res_ifname = NULL;
|
2019-10-01 12:52:03 -04:00
|
|
|
const virNetDevBandwidth *bw;
|
2019-10-01 13:56:35 -04:00
|
|
|
const virNetDevVPortProfile *prof;
|
2021-12-10 14:56:46 +01:00
|
|
|
g_autoptr(virLXCDriverConfig) cfg = virLXCDriverGetConfig(driver);
|
2014-11-18 15:55:48 -08:00
|
|
|
const char *linkdev = virDomainNetGetActualDirectDev(net);
|
2014-09-16 16:50:53 -04:00
|
|
|
unsigned int macvlan_create_flags = VIR_NETDEV_MACVLAN_CREATE_IFUP;
|
2012-07-13 12:39:29 +01: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 13:59:51 +01: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 12:39:29 +01: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 13:59:51 +01: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 12:39:29 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
if (virNetDevMacVLanCreateWithVPortProfile(
|
|
|
|
net->ifname, &net->mac,
|
2014-11-18 15:55:48 -08:00
|
|
|
linkdev,
|
2012-07-13 12:39:29 +01:00
|
|
|
virDomainNetGetActualDirectMode(net),
|
2016-05-04 13:18:16 -04:00
|
|
|
virDomainNetGetActualVlan(net),
|
2015-12-03 11:33:55 +01:00
|
|
|
def->uuid,
|
2014-11-18 15:55:48 -08:00
|
|
|
prof,
|
2012-07-13 12:39:29 +01:00
|
|
|
&res_ifname,
|
|
|
|
VIR_NETDEV_VPORT_PROFILE_OP_CREATE,
|
2014-09-16 16:50:53 -04:00
|
|
|
cfg->stateDir,
|
2015-12-04 11:31:17 +01:00
|
|
|
NULL, 0,
|
2014-09-16 16:50:53 -04:00
|
|
|
macvlan_create_flags) < 0)
|
2021-12-10 16:21:59 +01:00
|
|
|
return NULL;
|
2012-07-13 12:39:29 +01:00
|
|
|
|
2021-12-10 16:21:59 +01:00
|
|
|
return res_ifname;
|
2012-07-13 12:39:29 +01:00
|
|
|
}
|
|
|
|
|
2015-08-20 19:16:17 +05:30
|
|
|
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",
|
|
|
|
};
|
|
|
|
|
2021-03-11 08:16:13 +01:00
|
|
|
static int virLXCProcessSetupNamespaceName(virLXCDriver *driver,
|
2019-12-04 09:08:17 +01:00
|
|
|
int ns_type,
|
|
|
|
const char *name)
|
2015-08-20 19:16:17 +05:30
|
|
|
{
|
|
|
|
int fd = -1;
|
2021-03-11 08:16:13 +01:00
|
|
|
virDomainObj *vm;
|
|
|
|
virLXCDomainObjPrivate *priv;
|
2020-03-05 09:25:35 +01:00
|
|
|
g_autofree char *path = NULL;
|
2015-08-20 19:16:17 +05:30
|
|
|
|
|
|
|
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 15:26:14 +02:00
|
|
|
path = g_strdup_printf("/proc/%lld/ns/%s", (long long int)priv->initpid,
|
|
|
|
nsInfoLocal[ns_type]);
|
2015-08-20 19:16:17 +05:30
|
|
|
|
|
|
|
if ((fd = open(path, O_RDONLY)) < 0) {
|
|
|
|
virReportSystemError(errno,
|
|
|
|
_("failed to open ns %s"),
|
|
|
|
virLXCDomainNamespaceTypeToString(ns_type));
|
|
|
|
goto cleanup;
|
|
|
|
}
|
|
|
|
|
|
|
|
cleanup:
|
2018-03-10 08:47:44 -05:00
|
|
|
virDomainObjEndAPI(&vm);
|
2015-08-20 19:16:17 +05:30
|
|
|
return fd;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static int virLXCProcessSetupNamespacePID(int ns_type, const char *name)
|
|
|
|
{
|
2020-05-12 17:53:07 +01: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 19:16:17 +05:30
|
|
|
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 17:53:07 +01:00
|
|
|
g_autofree char *path = NULL;
|
2015-08-20 19:16:17 +05:30
|
|
|
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 15:26:14 +02:00
|
|
|
path = g_strdup_printf("%s/netns/%s", RUNSTATEDIR, name);
|
2015-08-20 19:16:17 +05:30
|
|
|
fd = open(path, O_RDONLY);
|
|
|
|
if (fd < 0) {
|
|
|
|
virReportSystemError(errno,
|
|
|
|
_("failed to open netns %s"), name);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
return fd;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
* virLXCProcessSetupNamespaces:
|
2019-12-04 09:08:17 +01:00
|
|
|
* @driver: pointer to driver structure
|
2015-08-20 19:16:17 +05:30
|
|
|
* @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 09:08:17 +01:00
|
|
|
static int
|
2021-03-11 08:16:13 +01:00
|
|
|
virLXCProcessSetupNamespaces(virLXCDriver *driver,
|
|
|
|
lxcDomainDef *lxcDef,
|
2019-12-04 09:08:17 +01:00
|
|
|
int *nsFDs)
|
2015-08-20 19:16:17 +05:30
|
|
|
{
|
|
|
|
size_t i;
|
|
|
|
|
|
|
|
for (i = 0; i < VIR_LXC_DOMAIN_NAMESPACE_LAST; i++)
|
|
|
|
nsFDs[i] = -1;
|
2020-01-24 21:30:04 +01:00
|
|
|
/* If there are no namespaces to be opened just return success */
|
2015-08-20 19:16:17 +05:30
|
|
|
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 09:08:17 +01:00
|
|
|
if ((nsFDs[i] = virLXCProcessSetupNamespaceName(driver, i,
|
|
|
|
lxcDef->ns_val[i])) < 0)
|
2015-08-20 19:16:17 +05:30
|
|
|
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 12:39:29 +01:00
|
|
|
|
|
|
|
/**
|
2012-07-13 12:49:24 +01:00
|
|
|
* virLXCProcessSetupInterfaces:
|
2019-12-04 09:08:17 +01:00
|
|
|
* @driver: pointer to driver structure
|
2012-07-13 12:39:29 +01:00
|
|
|
* @def: pointer to virtual machine structure
|
2018-07-26 17:25:58 +02:00
|
|
|
* @veths: string list of interface names
|
2012-07-13 12:39:29 +01: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 09:08:17 +01:00
|
|
|
static int
|
2021-03-11 08:16:13 +01:00
|
|
|
virLXCProcessSetupInterfaces(virLXCDriver *driver,
|
|
|
|
virDomainDef *def,
|
2019-12-04 09:08:17 +01:00
|
|
|
char ***veths)
|
2012-07-13 12:39:29 +01:00
|
|
|
{
|
|
|
|
int ret = -1;
|
|
|
|
size_t i;
|
2014-06-27 10:41:22 +02:00
|
|
|
size_t niface = 0;
|
2021-03-11 08:16:13 +01:00
|
|
|
virDomainNetDef *net;
|
2014-11-18 15:55:48 -08:00
|
|
|
virDomainNetType type;
|
2021-01-08 00:31:05 -05:00
|
|
|
g_autoptr(virConnect) netconn = NULL;
|
2019-04-30 16:17:14 +02:00
|
|
|
virErrorPtr save_err = NULL;
|
2012-07-13 12:39:29 +01:00
|
|
|
|
2020-05-12 18:31:16 +01:00
|
|
|
*veths = g_new0(char *, def->nnets + 1);
|
2018-07-26 17:25:58 +02:00
|
|
|
|
2013-05-21 16:03:33 +08:00
|
|
|
for (i = 0; i < def->nnets; i++) {
|
2012-11-27 12:55:02 +00:00
|
|
|
char *veth = NULL;
|
2019-10-01 12:52:03 -04:00
|
|
|
const virNetDevBandwidth *actualBandwidth;
|
2012-07-13 12:39:29 +01: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 15:55:48 -08:00
|
|
|
net = def->nets[i];
|
2016-05-13 13:20:54 -04:00
|
|
|
|
|
|
|
if (virLXCProcessValidateInterface(net) < 0)
|
2018-07-27 14:32:36 +02:00
|
|
|
goto cleanup;
|
2016-05-13 13:20:54 -04:00
|
|
|
|
2018-07-26 15:32:04 +01: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 12:39:29 +01: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 15:48:13 -04:00
|
|
|
/* final validation now that actual type is known */
|
|
|
|
if (virDomainActualNetDefValidate(net) < 0)
|
|
|
|
return -1;
|
|
|
|
|
2014-11-18 15:55:48 -08:00
|
|
|
type = virDomainNetGetActualType(net);
|
|
|
|
switch (type) {
|
2014-11-21 15:08:50 -05:00
|
|
|
case VIR_DOMAIN_NET_TYPE_NETWORK:
|
2012-07-13 12:39:29 +01:00
|
|
|
case VIR_DOMAIN_NET_TYPE_BRIDGE: {
|
2014-11-18 15:55:48 -08:00
|
|
|
const char *brname = virDomainNetGetActualBridgeName(net);
|
2012-07-13 12:39:29 +01:00
|
|
|
if (!brname) {
|
2012-07-13 13:59:51 +01:00
|
|
|
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
|
|
|
|
_("No bridge name specified"));
|
2012-07-13 12:39:29 +01:00
|
|
|
goto cleanup;
|
|
|
|
}
|
2016-05-13 13:20:54 -04:00
|
|
|
if (!(veth = virLXCProcessSetupInterfaceTap(def, net, brname)))
|
2012-07-13 12:39:29 +01:00
|
|
|
goto cleanup;
|
|
|
|
} break;
|
2016-05-13 13:20:54 -04:00
|
|
|
case VIR_DOMAIN_NET_TYPE_ETHERNET:
|
|
|
|
if (!(veth = virLXCProcessSetupInterfaceTap(def, net, NULL)))
|
|
|
|
goto cleanup;
|
|
|
|
break;
|
2012-07-13 12:39:29 +01:00
|
|
|
case VIR_DOMAIN_NET_TYPE_DIRECT:
|
2019-12-04 09:08:17 +01:00
|
|
|
if (!(veth = virLXCProcessSetupInterfaceDirect(driver, def, net)))
|
2012-07-13 12:39:29 +01:00
|
|
|
goto cleanup;
|
|
|
|
break;
|
|
|
|
|
2014-11-18 15:55:48 -08:00
|
|
|
case VIR_DOMAIN_NET_TYPE_USER:
|
2014-07-11 19:47:31 +02:00
|
|
|
case VIR_DOMAIN_NET_TYPE_VHOSTUSER:
|
2012-07-13 12:39:29 +01:00
|
|
|
case VIR_DOMAIN_NET_TYPE_SERVER:
|
|
|
|
case VIR_DOMAIN_NET_TYPE_CLIENT:
|
|
|
|
case VIR_DOMAIN_NET_TYPE_MCAST:
|
2015-08-29 16:19:10 -04:00
|
|
|
case VIR_DOMAIN_NET_TYPE_UDP:
|
2012-07-13 12:39:29 +01:00
|
|
|
case VIR_DOMAIN_NET_TYPE_INTERNAL:
|
|
|
|
case VIR_DOMAIN_NET_TYPE_LAST:
|
2014-11-18 15:55:48 -08:00
|
|
|
case VIR_DOMAIN_NET_TYPE_HOSTDEV:
|
2020-10-14 12:08:25 -05:00
|
|
|
case VIR_DOMAIN_NET_TYPE_VDPA:
|
2012-07-13 13:59:51 +01:00
|
|
|
virReportError(VIR_ERR_INTERNAL_ERROR,
|
|
|
|
_("Unsupported network type %s"),
|
2014-11-18 15:55:48 -08:00
|
|
|
virDomainNetTypeToString(type));
|
2012-07-13 12:39:29 +01:00
|
|
|
goto cleanup;
|
2014-11-18 15:55:48 -08:00
|
|
|
|
2012-07-13 12:39:29 +01:00
|
|
|
}
|
2012-11-27 12:55:02 +00:00
|
|
|
|
2015-01-07 15:52:21 +01:00
|
|
|
/* Set bandwidth or warn if requested and not supported. */
|
|
|
|
actualBandwidth = virDomainNetGetActualBandwidth(net);
|
|
|
|
if (actualBandwidth) {
|
2020-02-17 18:17:23 +01:00
|
|
|
if (virNetDevSupportsBandwidth(type)) {
|
2017-10-02 14:12:44 +02:00
|
|
|
if (virNetDevBandwidthSet(net->ifname, actualBandwidth, false,
|
|
|
|
!virDomainNetTypeSharesHostView(net)) < 0)
|
2015-01-07 15:52:21 +01:00
|
|
|
goto cleanup;
|
|
|
|
} else {
|
|
|
|
VIR_WARN("setting bandwidth on interfaces of "
|
|
|
|
"type '%s' is not implemented yet",
|
|
|
|
virDomainNetTypeToString(type));
|
|
|
|
}
|
|
|
|
}
|
2014-11-18 15:55:48 -08:00
|
|
|
|
2018-07-26 17:25:58 +02:00
|
|
|
(*veths)[i] = veth;
|
2014-06-27 10:41:22 +02:00
|
|
|
|
2019-10-20 13:49:46 +02:00
|
|
|
def->nets[i]->ifname_guest_actual = g_strdup(veth);
|
2014-12-19 10:08:38 +01:00
|
|
|
|
2014-06-27 10:41:22 +02:00
|
|
|
/* Make sure all net definitions will have a name in the container */
|
2014-11-18 15:55:48 -08:00
|
|
|
if (!net->ifname_guest) {
|
2019-10-22 15:26:14 +02:00
|
|
|
net->ifname_guest = g_strdup_printf("eth%zu", niface);
|
2014-06-27 10:41:22 +02:00
|
|
|
niface++;
|
|
|
|
}
|
2012-07-13 12:39:29 +01:00
|
|
|
}
|
|
|
|
|
2012-11-23 16:38:01 +00:00
|
|
|
ret = 0;
|
2012-07-13 12:39:29 +01:00
|
|
|
|
2014-03-25 07:49:26 +01:00
|
|
|
cleanup:
|
2012-11-23 16:38:01 +00:00
|
|
|
if (ret < 0) {
|
2019-04-30 16:17:14 +02:00
|
|
|
virErrorPreserveLast(&save_err);
|
2013-05-21 16:03:33 +08:00
|
|
|
for (i = 0; i < def->nnets; i++) {
|
2021-03-11 08:16:13 +01:00
|
|
|
virDomainNetDef *iface = def->nets[i];
|
2019-10-01 13:56:35 -04:00
|
|
|
const virNetDevVPortProfile *vport = virDomainNetGetActualVirtPortProfile(iface);
|
2012-07-13 12:39:29 +01:00
|
|
|
if (vport && vport->virtPortType == VIR_NETDEV_VPORT_PROFILE_OPENVSWITCH)
|
|
|
|
ignore_value(virNetDevOpenvswitchRemovePort(
|
|
|
|
virDomainNetGetActualBridgeName(iface),
|
|
|
|
iface->ifname));
|
2018-07-26 15:32:04 +01:00
|
|
|
if (iface->type == VIR_DOMAIN_NET_TYPE_NETWORK && netconn)
|
|
|
|
virDomainNetReleaseActualDevice(netconn, def, iface);
|
2012-07-13 12:39:29 +01:00
|
|
|
}
|
2019-04-30 16:17:14 +02:00
|
|
|
virErrorRestore(&save_err);
|
2012-07-13 12:39:29 +01:00
|
|
|
}
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2014-06-27 10:41:22 +02:00
|
|
|
static void
|
2021-03-11 08:16:13 +01:00
|
|
|
virLXCProcessCleanInterfaces(virDomainDef *def)
|
2014-06-27 10:41:22 +02:00
|
|
|
{
|
|
|
|
size_t i;
|
|
|
|
|
|
|
|
for (i = 0; i < def->nnets; i++) {
|
2022-01-28 18:42:45 +01:00
|
|
|
g_clear_pointer(&def->nets[i]->ifname_guest_actual, g_free);
|
2014-06-27 10:41:22 +02:00
|
|
|
VIR_DEBUG("Cleared net names: %s", def->nets[i]->ifname_guest);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2012-07-13 12:39:29 +01:00
|
|
|
|
2021-03-11 08:16:13 +01:00
|
|
|
extern virLXCDriver *lxc_driver;
|
|
|
|
static void virLXCProcessMonitorEOFNotify(virLXCMonitor *mon,
|
|
|
|
virDomainObj *vm)
|
2012-07-17 11:55:38 +01:00
|
|
|
{
|
2021-03-11 08:16:13 +01:00
|
|
|
virLXCDriver *driver = lxc_driver;
|
|
|
|
virObjectEvent *event = NULL;
|
|
|
|
virLXCDomainObjPrivate *priv;
|
2012-07-17 11:55:38 +01:00
|
|
|
|
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 15:08:20 +01:00
|
|
|
VIR_DEBUG("mon=%p vm=%p", mon, vm);
|
|
|
|
|
2013-01-09 21:00:32 +00:00
|
|
|
virObjectLock(vm);
|
2012-07-17 11:55:38 +01:00
|
|
|
|
|
|
|
priv = vm->privateData;
|
|
|
|
virLXCProcessStop(driver, vm, VIR_DOMAIN_SHUTOFF_SHUTDOWN);
|
2012-07-23 12:25:37 +01:00
|
|
|
if (!priv->wantReboot) {
|
|
|
|
virLXCProcessStop(driver, vm, VIR_DOMAIN_SHUTOFF_SHUTDOWN);
|
|
|
|
if (!priv->doneStopEvent) {
|
2013-11-21 18:03:26 +01:00
|
|
|
event = virDomainEventLifecycleNewFromObj(vm,
|
2012-07-23 12:25:37 +01:00
|
|
|
VIR_DOMAIN_EVENT_STOPPED,
|
|
|
|
priv->stopReason);
|
|
|
|
virDomainAuditStop(vm, "shutdown");
|
|
|
|
} else {
|
|
|
|
VIR_DEBUG("Stop event has already been sent");
|
|
|
|
}
|
2018-04-23 10:40:48 -04:00
|
|
|
if (!vm->persistent)
|
2013-01-11 16:04:47 +00:00
|
|
|
virDomainObjListRemove(driver->domains, vm);
|
2012-07-17 11:55:38 +01:00
|
|
|
} else {
|
2012-07-23 12:25:37 +01:00
|
|
|
int ret = virLXCProcessReboot(driver, vm);
|
|
|
|
virDomainAuditStop(vm, "reboot");
|
|
|
|
virDomainAuditStart(vm, "reboot", ret == 0);
|
|
|
|
if (ret == 0) {
|
|
|
|
event = virDomainEventRebootNewFromObj(vm);
|
|
|
|
} else {
|
2013-11-21 18:03:26 +01:00
|
|
|
event = virDomainEventLifecycleNewFromObj(vm,
|
2012-07-23 12:25:37 +01:00
|
|
|
VIR_DOMAIN_EVENT_STOPPED,
|
|
|
|
priv->stopReason);
|
2018-04-23 10:40:48 -04:00
|
|
|
if (!vm->persistent)
|
2013-01-11 16:04:47 +00:00
|
|
|
virDomainObjListRemove(driver->domains, vm);
|
2012-07-23 12:25:37 +01:00
|
|
|
}
|
2012-07-17 11:55:38 +01:00
|
|
|
}
|
|
|
|
|
2018-04-23 10:40:48 -04: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 15:38:17 -04:00
|
|
|
virObjectEventStateQueue(driver->domainEventState, event);
|
2012-07-17 11:55:38 +01:00
|
|
|
}
|
|
|
|
|
2021-03-11 08:16:13 +01:00
|
|
|
static void virLXCProcessMonitorExitNotify(virLXCMonitor *mon G_GNUC_UNUSED,
|
2013-03-13 19:19:22 +00:00
|
|
|
virLXCMonitorExitStatus status,
|
2021-03-11 08:16:13 +01:00
|
|
|
virDomainObj *vm)
|
2012-07-17 15:54:08 +01:00
|
|
|
{
|
2021-03-11 08:16:13 +01:00
|
|
|
virLXCDomainObjPrivate *priv = vm->privateData;
|
2012-07-17 15:54:08 +01:00
|
|
|
|
2013-03-22 12:10:39 +00:00
|
|
|
virObjectLock(vm);
|
|
|
|
|
2012-07-17 15:54:08 +01:00
|
|
|
switch (status) {
|
2013-03-13 19:19:22 +00:00
|
|
|
case VIR_LXC_MONITOR_EXIT_STATUS_SHUTDOWN:
|
2012-07-17 15:54:08 +01: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 15:54:08 +01: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 12:25:37 +01:00
|
|
|
priv->stopReason = VIR_DOMAIN_EVENT_STOPPED_SHUTDOWN;
|
|
|
|
priv->wantReboot = true;
|
|
|
|
break;
|
2012-07-17 15:54:08 +01: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 15:54:08 +01:00
|
|
|
}
|
2012-07-17 11:55:38 +01:00
|
|
|
|
2013-03-06 14:56:49 +00:00
|
|
|
static int
|
|
|
|
virLXCProcessGetNsInode(pid_t pid,
|
|
|
|
const char *nsname,
|
|
|
|
ino_t *inode)
|
|
|
|
{
|
2020-05-12 17:53:07 +01:00
|
|
|
g_autofree char *path = NULL;
|
2013-03-06 14:56:49 +00:00
|
|
|
struct stat sb;
|
|
|
|
|
2019-10-22 15:26:14 +02: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 17:53:07 +01:00
|
|
|
return -1;
|
2013-03-06 14:56:49 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
*inode = sb.st_ino;
|
|
|
|
|
2020-05-12 17:53:07 +01:00
|
|
|
return 0;
|
2013-03-06 14:56:49 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2012-11-22 17:15:54 +00:00
|
|
|
/* XXX a little evil */
|
2021-03-11 08:16:13 +01:00
|
|
|
extern virLXCDriver *lxc_driver;
|
|
|
|
static void virLXCProcessMonitorInitNotify(virLXCMonitor *mon G_GNUC_UNUSED,
|
2012-11-20 17:49:25 +00:00
|
|
|
pid_t initpid,
|
2021-03-11 08:16:13 +01:00
|
|
|
virDomainObj *vm)
|
2012-11-20 17:49:25 +00:00
|
|
|
{
|
2021-03-11 08:16:13 +01:00
|
|
|
virLXCDriver *driver = lxc_driver;
|
|
|
|
virLXCDomainObjPrivate *priv;
|
2021-12-10 14:56:46 +01:00
|
|
|
g_autoptr(virLXCDriverConfig) cfg = virLXCDriverGetConfig(driver);
|
2016-03-04 15:21:36 +01: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 16:54:41 +02:00
|
|
|
VIR_WARN("Cannot obtain pid NS inode for %lld: %s",
|
2018-04-25 14:42:34 +02:00
|
|
|
(long long)initpid,
|
2016-05-19 21:10:19 +02: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);
|
2012-11-20 17:49:25 +00:00
|
|
|
}
|
|
|
|
|
2012-07-17 12:14:35 +01:00
|
|
|
static virLXCMonitorCallbacks monitorCallbacks = {
|
|
|
|
.eofNotify = virLXCProcessMonitorEOFNotify,
|
2012-07-17 15:54:08 +01:00
|
|
|
.exitNotify = virLXCProcessMonitorExitNotify,
|
2012-11-20 17:49:25 +00:00
|
|
|
.initNotify = virLXCProcessMonitorInitNotify,
|
2012-07-17 12:14:35 +01:00
|
|
|
};
|
2012-07-13 12:39:29 +01:00
|
|
|
|
2012-07-17 12:14:35 +01:00
|
|
|
|
2021-03-11 08:16:13 +01:00
|
|
|
static virLXCMonitor *virLXCProcessConnectMonitor(virLXCDriver *driver,
|
|
|
|
virDomainObj *vm)
|
2012-07-17 12:14:35 +01:00
|
|
|
{
|
2021-03-11 08:16:13 +01:00
|
|
|
virLXCMonitor *monitor = NULL;
|
2021-12-10 14:56:46 +01:00
|
|
|
g_autoptr(virLXCDriverConfig) cfg = virLXCDriverGetConfig(driver);
|
2012-07-13 12:39:29 +01:00
|
|
|
|
2012-07-17 11:55:38 +01:00
|
|
|
if (virSecurityManagerSetSocketLabel(driver->securityManager, vm->def) < 0)
|
2021-12-10 16:21:59 +01:00
|
|
|
return NULL;
|
2012-07-13 12:39:29 +01:00
|
|
|
|
2012-07-17 12:14:35 +01:00
|
|
|
/* Hold an extra reference because we can't allow 'vm' to be
|
2018-04-23 10:40:48 -04:00
|
|
|
* deleted while the monitor is active. This will be unreffed
|
|
|
|
* during EOFNotify processing. */
|
2012-07-11 14:35:46 +01:00
|
|
|
virObjectRef(vm);
|
2012-07-17 12:14:35 +01:00
|
|
|
|
2013-07-16 17:45:05 +02:00
|
|
|
monitor = virLXCMonitorNew(vm, cfg->stateDir, &monitorCallbacks);
|
2012-07-17 12:14:35 +01:00
|
|
|
|
|
|
|
if (monitor == NULL)
|
2012-07-11 14:35:46 +01:00
|
|
|
virObjectUnref(vm);
|
2012-07-13 12:39:29 +01:00
|
|
|
|
|
|
|
if (virSecurityManagerClearSocketLabel(driver->securityManager, vm->def) < 0) {
|
2021-12-10 16:21:59 +01:00
|
|
|
if (monitor)
|
2012-09-21 15:09:42 +01:00
|
|
|
virObjectUnref(monitor);
|
2021-12-10 16:21:59 +01:00
|
|
|
return NULL;
|
2012-07-13 12:39:29 +01:00
|
|
|
}
|
|
|
|
|
2012-07-17 11:55:38 +01:00
|
|
|
return monitor;
|
2012-07-13 12:39:29 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2021-03-11 08:16:13 +01:00
|
|
|
int virLXCProcessStop(virLXCDriver *driver,
|
|
|
|
virDomainObj *vm,
|
2012-07-13 12:49:24 +01:00
|
|
|
virDomainShutoffReason reason)
|
2012-07-13 12:39:29 +01:00
|
|
|
{
|
|
|
|
int rc;
|
2021-03-11 08:16:13 +01:00
|
|
|
virLXCDomainObjPrivate *priv;
|
2012-07-13 12:39:29 +01:00
|
|
|
|
2012-07-17 15:54:08 +01: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 16:42:48 +01: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 12:42:21 +08:00
|
|
|
* here because the domain has already been suspended using the
|
2014-02-14 16:42:48 +01: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 15:43:04 +01:00
|
|
|
if (rc < 0)
|
|
|
|
return -1;
|
|
|
|
if (rc > 0) {
|
2012-07-13 13:59:51 +01:00
|
|
|
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
|
2013-07-19 15:43:04 +01:00
|
|
|
_("Some processes refused to die"));
|
|
|
|
return -1;
|
2012-07-13 12:39:29 +01:00
|
|
|
}
|
2017-07-21 15:56:46 +02:00
|
|
|
} else if (vm->pid > 0) {
|
2013-07-22 16:36:06 +01:00
|
|
|
/* If cgroup doesn't exist, just try cleaning up the
|
|
|
|
* libvirt_lxc process */
|
2013-12-13 16:30:36 +08: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 12:39:29 +01:00
|
|
|
}
|
|
|
|
|
2014-03-25 07:49:26 +01:00
|
|
|
cleanup:
|
2020-11-11 13:51:21 +01:00
|
|
|
virLXCProcessCleanup(driver, vm, reason, 0);
|
2012-07-13 12:39:29 +01:00
|
|
|
|
2013-07-19 15:43:04 +01:00
|
|
|
return 0;
|
2012-07-13 12:39:29 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2021-03-11 08:16:13 +01:00
|
|
|
static virCommand *
|
|
|
|
virLXCProcessBuildControllerCmd(virLXCDriver *driver,
|
|
|
|
virDomainObj *vm,
|
2012-07-13 12:49:24 +01:00
|
|
|
char **veths,
|
|
|
|
int *ttyFDs,
|
|
|
|
size_t nttyFDs,
|
2015-08-20 19:16:17 +05:30
|
|
|
int *nsInheritFDs,
|
2013-07-09 18:15:45 +01:00
|
|
|
int *files,
|
|
|
|
size_t nfiles,
|
2021-04-20 13:28:20 +02:00
|
|
|
int handshakefdW,
|
|
|
|
int handshakefdR,
|
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 17:21:28 +02:00
|
|
|
int * const logfd,
|
2015-01-16 15:03:16 +00:00
|
|
|
const char *pidfile)
|
2012-07-13 12:39:29 +01:00
|
|
|
{
|
|
|
|
size_t i;
|
2020-05-12 17:53:07 +01:00
|
|
|
g_autofree char *filterstr = NULL;
|
|
|
|
g_autofree char *outputstr = NULL;
|
2021-12-10 14:49:23 +01:00
|
|
|
g_autoptr(virCommand) cmd = NULL;
|
2021-12-10 14:56:46 +01:00
|
|
|
g_autoptr(virLXCDriverConfig) cfg = virLXCDriverGetConfig(driver);
|
2012-07-13 12:39:29 +01:00
|
|
|
|
|
|
|
cmd = virCommandNew(vm->def->emulator);
|
|
|
|
|
|
|
|
/* The controller may call ip command, so we have to retain PATH. */
|
2019-08-01 13:52:00 +01:00
|
|
|
virCommandAddEnvPass(cmd, "PATH");
|
2012-07-13 12:39:29 +01:00
|
|
|
|
|
|
|
virCommandAddEnvFormat(cmd, "LIBVIRT_DEBUG=%d",
|
|
|
|
virLogGetDefaultPriority());
|
|
|
|
|
|
|
|
if (virLogGetNbFilters() > 0) {
|
|
|
|
filterstr = virLogGetFilters();
|
|
|
|
|
|
|
|
virCommandAddEnvPair(cmd, "LIBVIRT_LOG_FILTERS", filterstr);
|
|
|
|
}
|
|
|
|
|
2013-07-16 17:45:05 +02:00
|
|
|
if (cfg->log_libvirtd) {
|
2012-07-13 12:39:29 +01:00
|
|
|
if (virLogGetNbOutputs() > 0) {
|
2021-02-23 17:13:54 +01:00
|
|
|
if (!(outputstr = virLogGetOutputs()))
|
2021-12-10 14:49:23 +01:00
|
|
|
return NULL;
|
2012-07-13 12:39:29 +01: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 16:03:33 +08:00
|
|
|
for (i = 0; i < nttyFDs; i++) {
|
2012-07-13 12:39:29 +01:00
|
|
|
virCommandAddArg(cmd, "--console");
|
|
|
|
virCommandAddArgFormat(cmd, "%d", ttyFDs[i]);
|
2013-07-11 11:31:56 +01:00
|
|
|
virCommandPassFD(cmd, ttyFDs[i], 0);
|
2012-07-13 12:39:29 +01:00
|
|
|
}
|
|
|
|
|
2013-07-09 18:15:45 +01:00
|
|
|
for (i = 0; i < nfiles; i++) {
|
|
|
|
virCommandAddArg(cmd, "--passfd");
|
|
|
|
virCommandAddArgFormat(cmd, "%d", files[i]);
|
2013-07-11 11:31:56 +01:00
|
|
|
virCommandPassFD(cmd, files[i], 0);
|
2013-07-09 18:15:45 +01:00
|
|
|
}
|
|
|
|
|
2015-08-20 19:16:17 +05:30
|
|
|
for (i = 0; i < VIR_LXC_DOMAIN_NAMESPACE_LAST; i++) {
|
|
|
|
if (nsInheritFDs[i] > 0) {
|
2020-05-12 17:53:07 +01:00
|
|
|
g_autofree char *tmp = g_strdup_printf("--share-%s",
|
|
|
|
nsInfoLocal[i]);
|
2015-08-20 19:16:17 +05:30
|
|
|
virCommandAddArg(cmd, tmp);
|
|
|
|
virCommandAddArgFormat(cmd, "%d", nsInheritFDs[i]);
|
|
|
|
virCommandPassFD(cmd, nsInheritFDs[i], 0);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2012-07-13 12:39:29 +01:00
|
|
|
virCommandAddArgPair(cmd, "--security",
|
|
|
|
virSecurityManagerGetModel(driver->securityManager));
|
|
|
|
|
2021-04-20 13:28:20 +02:00
|
|
|
virCommandAddArg(cmd, "--handshakefds");
|
|
|
|
virCommandAddArgFormat(cmd, "%d:%d", handshakefdR, handshakefdW);
|
2012-07-13 12:39:29 +01:00
|
|
|
|
2018-07-26 17:25:58 +02:00
|
|
|
for (i = 0; veths && veths[i]; i++)
|
2012-07-13 12:39:29 +01:00
|
|
|
virCommandAddArgList(cmd, "--veth", veths[i], NULL);
|
|
|
|
|
2021-04-20 13:28:20 +02:00
|
|
|
virCommandPassFD(cmd, handshakefdW, 0);
|
|
|
|
virCommandPassFD(cmd, handshakefdR, 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 17:21:28 +02: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 12:39:29 +01:00
|
|
|
|
2021-12-10 14:49:23 +01:00
|
|
|
return g_steal_pointer(&cmd);
|
2012-07-13 12:39:29 +01:00
|
|
|
}
|
|
|
|
|
2013-03-04 20:47:37 +00:00
|
|
|
|
2013-10-14 13:07:22 +01: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 12:39:29 +01:00
|
|
|
static int
|
2021-03-11 08:16:13 +01:00
|
|
|
virLXCProcessReadLogOutputData(virDomainObj *vm,
|
2013-03-04 20:47:37 +00:00
|
|
|
int fd,
|
|
|
|
char *buf,
|
|
|
|
size_t buflen)
|
2012-07-13 12:39:29 +01:00
|
|
|
{
|
|
|
|
int retries = 10;
|
2013-03-04 20:47:37 +00:00
|
|
|
int got = 0;
|
|
|
|
char *filter_next = buf;
|
2012-07-13 12:39:29 +01:00
|
|
|
|
2013-03-04 20:47:37 +00:00
|
|
|
buf[0] = '\0';
|
2012-07-13 12:39:29 +01:00
|
|
|
|
|
|
|
while (retries) {
|
|
|
|
ssize_t bytes;
|
2013-05-24 12:14:02 +02:00
|
|
|
bool isdead = false;
|
2013-03-04 20:47:37 +00:00
|
|
|
char *eol;
|
2012-07-13 12:39:29 +01:00
|
|
|
|
2013-03-04 20:47:37 +00:00
|
|
|
if (vm->pid <= 0 ||
|
|
|
|
(kill(vm->pid, 0) == -1 && errno == ESRCH))
|
2013-05-24 12:14:02 +02:00
|
|
|
isdead = true;
|
2012-07-13 12:39:29 +01: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 15:18:55 -03:00
|
|
|
return -1;
|
2012-07-13 12:39:29 +01: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 13:07:22 +01: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 15:18:55 -03:00
|
|
|
return -1;
|
2013-03-04 20:47:37 +00:00
|
|
|
}
|
|
|
|
|
2019-10-21 15:18:55 -03:00
|
|
|
if (isdead)
|
|
|
|
return got;
|
2012-07-13 12:39:29 +01:00
|
|
|
|
2019-10-02 18:01:11 +01:00
|
|
|
g_usleep(100*1000);
|
2012-07-13 12:39:29 +01: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 12:39:29 +01:00
|
|
|
|
2019-10-21 15:18:55 -03:00
|
|
|
return -1;
|
2013-03-04 20:47:37 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static int
|
2021-03-11 08:16:13 +01:00
|
|
|
virLXCProcessReadLogOutput(virDomainObj *vm,
|
2013-03-04 20:47:37 +00:00
|
|
|
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 12:39:29 +01:00
|
|
|
VIR_FORCE_CLOSE(fd);
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2013-04-03 16:19:24 +01:00
|
|
|
|
|
|
|
static int
|
2021-03-11 08:16:13 +01:00
|
|
|
virLXCProcessEnsureRootFS(virDomainObj *vm)
|
2013-04-03 16:19:24 +01:00
|
|
|
{
|
2021-03-11 08:16:13 +01:00
|
|
|
virDomainFSDef *root = virDomainGetFilesystemForTarget(vm->def, "/");
|
2013-04-03 16:19:24 +01:00
|
|
|
|
|
|
|
if (root)
|
|
|
|
return 0;
|
|
|
|
|
2019-12-10 13:51:54 +01:00
|
|
|
if (!(root = virDomainFSDefNew(NULL)))
|
2013-07-04 12:11:37 +02:00
|
|
|
goto error;
|
2013-04-03 16:19:24 +01:00
|
|
|
|
|
|
|
root->type = VIR_DOMAIN_FS_TYPE_MOUNT;
|
|
|
|
|
2019-10-20 13:49:46 +02:00
|
|
|
root->src->path = g_strdup("/");
|
|
|
|
root->dst = g_strdup("/");
|
2013-04-03 16:19:24 +01:00
|
|
|
|
2013-07-04 12:11:37 +02:00
|
|
|
if (VIR_INSERT_ELEMENT(vm->def->fss,
|
|
|
|
0,
|
|
|
|
vm->def->nfss,
|
|
|
|
root) < 0)
|
|
|
|
goto error;
|
2013-04-03 16:19:24 +01:00
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
2014-03-25 07:49:26 +01:00
|
|
|
error:
|
2013-04-03 16:19:24 +01:00
|
|
|
virDomainFSDefFree(root);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
2012-07-13 12:39:29 +01:00
|
|
|
/**
|
2012-07-13 12:49:24 +01:00
|
|
|
* virLXCProcessStart:
|
2012-07-13 12:39:29 +01: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 12:49:24 +01:00
|
|
|
int virLXCProcessStart(virConnectPtr conn,
|
2021-03-11 08:16:13 +01:00
|
|
|
virLXCDriver * driver,
|
|
|
|
virDomainObj *vm,
|
2013-07-09 18:15:45 +01:00
|
|
|
unsigned int nfiles, int *files,
|
2012-07-13 12:49:24 +01:00
|
|
|
bool autoDestroy,
|
|
|
|
virDomainRunningReason reason)
|
2012-07-13 12:39:29 +01:00
|
|
|
{
|
|
|
|
int rc = -1, r;
|
|
|
|
size_t nttyFDs = 0;
|
2020-05-12 17:53:07 +01:00
|
|
|
g_autofree int *ttyFDs = NULL;
|
2012-07-13 12:39:29 +01:00
|
|
|
size_t i;
|
2020-05-12 17:53:07 +01:00
|
|
|
g_autofree char *logfile = NULL;
|
2012-07-13 12:39:29 +01:00
|
|
|
int logfd = -1;
|
2020-12-01 09:21:32 +01:00
|
|
|
g_auto(GStrv) veths = NULL;
|
2021-04-20 13:28:20 +02:00
|
|
|
int handshakefds[4] = { -1, -1, -1, -1 }; /* two pipes */
|
2012-07-13 12:39:29 +01:00
|
|
|
off_t pos = -1;
|
|
|
|
char ebuf[1024];
|
2020-05-12 17:53:07 +01:00
|
|
|
g_autofree char *timestamp = NULL;
|
2015-08-20 19:16:17 +05:30
|
|
|
int nsInheritFDs[VIR_LXC_DOMAIN_NAMESPACE_LAST];
|
2021-12-13 14:04:47 +01:00
|
|
|
g_autoptr(virCommand) cmd = NULL;
|
2021-03-11 08:16:13 +01:00
|
|
|
virLXCDomainObjPrivate *priv = vm->privateData;
|
2021-12-10 15:04:07 +01:00
|
|
|
g_autoptr(virCaps) caps = NULL;
|
2012-07-13 12:39:29 +01:00
|
|
|
virErrorPtr err = NULL;
|
2021-12-10 14:56:46 +01:00
|
|
|
g_autoptr(virLXCDriverConfig) cfg = virLXCDriverGetConfig(driver);
|
2020-09-22 12:49:58 +02:00
|
|
|
g_autoptr(virCgroup) selfcgroup = NULL;
|
2013-10-11 17:30:53 +01:00
|
|
|
int status;
|
2020-05-12 17:53:07 +01:00
|
|
|
g_autofree char *pidfile = NULL;
|
2020-11-11 13:51:21 +01:00
|
|
|
unsigned int stopFlags = 0;
|
2012-07-13 12:39:29 +01:00
|
|
|
|
2013-07-22 16:36:06 +01:00
|
|
|
if (virCgroupNewSelf(&selfcgroup) < 0)
|
2012-07-13 12:39:29 +01:00
|
|
|
return -1;
|
|
|
|
|
2013-07-22 16:36:06 +01:00
|
|
|
if (!virCgroupHasController(selfcgroup,
|
2013-03-21 14:40:29 +00:00
|
|
|
VIR_CGROUP_CONTROLLER_CPUACCT)) {
|
2012-07-13 13:59:51 +01:00
|
|
|
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
|
|
|
|
_("Unable to find 'cpuacct' cgroups controller mount"));
|
2012-07-13 12:39:29 +01:00
|
|
|
return -1;
|
|
|
|
}
|
2013-07-22 16:36:06 +01:00
|
|
|
if (!virCgroupHasController(selfcgroup,
|
2013-03-21 13:38:31 +00:00
|
|
|
VIR_CGROUP_CONTROLLER_DEVICES)) {
|
2012-07-13 13:59:51 +01:00
|
|
|
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
|
|
|
|
_("Unable to find 'devices' cgroups controller mount"));
|
2012-07-13 12:39:29 +01:00
|
|
|
return -1;
|
|
|
|
}
|
2013-07-22 16:36:06 +01:00
|
|
|
if (!virCgroupHasController(selfcgroup,
|
2013-03-21 13:38:31 +00:00
|
|
|
VIR_CGROUP_CONTROLLER_MEMORY)) {
|
2012-07-13 13:59:51 +01:00
|
|
|
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
|
|
|
|
_("Unable to find 'memory' cgroups controller mount"));
|
2012-07-13 12:39:29 +01:00
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
2015-02-12 14:43:26 -05: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 07:45:54 -04:00
|
|
|
if (vm->def->consoles[i]->source->type != VIR_DOMAIN_CHR_TYPE_PTY) {
|
2015-02-12 14:43:26 -05:00
|
|
|
virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
|
|
|
|
_("Only PTY console types are supported"));
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-02-26 09:37:10 +01:00
|
|
|
if (g_mkdir_with_parents(cfg->logDir, 0777) < 0) {
|
2012-07-13 12:39:29 +01:00
|
|
|
virReportSystemError(errno,
|
|
|
|
_("Cannot create log directory '%s'"),
|
2013-07-16 17:45:05 +02:00
|
|
|
cfg->logDir);
|
2012-07-13 12:39:29 +01:00
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
2021-08-25 13:01:52 +02:00
|
|
|
if (!vm->def->resource)
|
|
|
|
vm->def->resource = g_new0(virDomainResourceDef, 1);
|
2013-07-26 15:59:16 +01:00
|
|
|
|
2021-08-25 13:01:52 +02:00
|
|
|
if (!vm->def->resource->partition)
|
|
|
|
vm->def->resource->partition = g_strdup("/machine");
|
2013-07-26 15:59:16 +01:00
|
|
|
|
2019-10-22 15:26:14 +02: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 12:39:29 +01:00
|
|
|
|
2013-07-15 11:43:10 +02:00
|
|
|
if (!(caps = virLXCDriverGetCapabilities(driver, false)))
|
|
|
|
goto cleanup;
|
|
|
|
|
2012-07-13 12:39:29 +01: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 12:39:29 +01:00
|
|
|
goto cleanup;
|
2020-11-11 13:51:21 +01:00
|
|
|
stopFlags |= VIR_LXC_PROCESS_CLEANUP_REMOVE_TRANSIENT;
|
2012-07-13 12:39:29 +01:00
|
|
|
|
|
|
|
/* Run an early hook to set-up missing devices */
|
|
|
|
if (virHookPresent(VIR_HOOK_DRIVER_LXC)) {
|
2020-05-12 17:53:07 +01:00
|
|
|
g_autofree char *xml = virDomainDefFormat(vm->def, driver->xmlopt, 0);
|
2012-07-13 12:39:29 +01:00
|
|
|
|
|
|
|
/*
|
|
|
|
* If the script raised an error abort the launch
|
|
|
|
*/
|
2020-05-12 17:53:07 +01: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 12:39:29 +01:00
|
|
|
goto cleanup;
|
|
|
|
}
|
|
|
|
|
2013-04-03 16:19:24 +01: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 12:39:29 +01: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 18:31:16 +01:00
|
|
|
ttyFDs = g_new0(int, nttyFDs);
|
2013-05-21 16:03:33 +08:00
|
|
|
for (i = 0; i < vm->def->nconsoles; i++)
|
2013-03-13 17:30:31 +00:00
|
|
|
ttyFDs[i] = -1;
|
2012-07-13 12:39:29 +01: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 14:13:43 +01:00
|
|
|
|
2012-08-15 19:10:35 -03: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 12:39:29 +01:00
|
|
|
|
2015-02-12 18:32:40 +01:00
|
|
|
if (virSecurityManagerCheckAllLabel(driver->securityManager, vm->def) < 0)
|
|
|
|
goto cleanup;
|
|
|
|
|
2012-07-13 12:39:29 +01:00
|
|
|
if (virSecurityManagerGenLabel(driver->securityManager, vm->def) < 0) {
|
|
|
|
virDomainAuditSecurityLabel(vm, false);
|
|
|
|
goto cleanup;
|
|
|
|
}
|
|
|
|
virDomainAuditSecurityLabel(vm, true);
|
2020-11-11 13:51:21 +01:00
|
|
|
stopFlags |= VIR_LXC_PROCESS_CLEANUP_RELEASE_SECLABEL;
|
2012-07-13 12:39:29 +01:00
|
|
|
|
|
|
|
VIR_DEBUG("Setting domain security labels");
|
|
|
|
if (virSecurityManagerSetAllLabel(driver->securityManager,
|
2019-09-11 07:53:09 +02:00
|
|
|
vm->def, NULL, false, false) < 0)
|
2012-07-13 12:39:29 +01:00
|
|
|
goto cleanup;
|
2020-11-11 13:51:21 +01:00
|
|
|
stopFlags |= VIR_LXC_PROCESS_CLEANUP_RESTORE_SECLABEL;
|
2012-07-13 12:39:29 +01:00
|
|
|
|
2015-02-12 14:32:39 -05:00
|
|
|
VIR_DEBUG("Setting up consoles");
|
2013-05-21 16:03:33 +08:00
|
|
|
for (i = 0; i < vm->def->nconsoles; i++) {
|
2012-07-13 12:39:29 +01:00
|
|
|
char *ttyPath;
|
|
|
|
|
|
|
|
if (virFileOpenTty(&ttyFDs[i], &ttyPath, 1) < 0) {
|
|
|
|
virReportSystemError(errno, "%s",
|
|
|
|
_("Failed to allocate tty"));
|
|
|
|
goto cleanup;
|
|
|
|
}
|
|
|
|
|
2020-05-12 17:53:07 +01:00
|
|
|
g_free(vm->def->consoles[i]->source->data.file.path);
|
2016-10-21 07:45:54 -04:00
|
|
|
vm->def->consoles[i]->source->data.file.path = ttyPath;
|
2012-07-13 12:39:29 +01:00
|
|
|
|
2020-05-12 17:53:07 +01:00
|
|
|
g_free(vm->def->consoles[i]->info.alias);
|
2019-10-22 15:26:14 +02:00
|
|
|
vm->def->consoles[i]->info.alias = g_strdup_printf("console%zu", i);
|
2012-07-13 12:39:29 +01:00
|
|
|
}
|
|
|
|
|
2015-02-12 14:32:39 -05:00
|
|
|
VIR_DEBUG("Setting up Interfaces");
|
2019-12-04 09:08:17 +01:00
|
|
|
if (virLXCProcessSetupInterfaces(driver, vm->def, &veths) < 0)
|
2012-07-13 12:39:29 +01:00
|
|
|
goto cleanup;
|
|
|
|
|
2015-08-20 19:16:17 +05:30
|
|
|
VIR_DEBUG("Setting up namespaces if any");
|
2019-12-04 09:08:17 +01:00
|
|
|
if (virLXCProcessSetupNamespaces(driver, vm->def->namespaceData, nsInheritFDs) < 0)
|
2015-08-20 19:16:17 +05:30
|
|
|
goto cleanup;
|
|
|
|
|
2015-02-12 14:32:39 -05:00
|
|
|
VIR_DEBUG("Preparing to launch");
|
2012-07-13 12:39:29 +01: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;
|
|
|
|
}
|
|
|
|
|
2021-04-20 13:28:20 +02:00
|
|
|
if (virPipe(&handshakefds[0]) < 0 ||
|
|
|
|
virPipe(&handshakefds[2]) < 0)
|
2012-07-13 12:39:29 +01:00
|
|
|
goto cleanup;
|
|
|
|
|
2012-07-13 12:49:24 +01:00
|
|
|
if (!(cmd = virLXCProcessBuildControllerCmd(driver,
|
|
|
|
vm,
|
2018-07-26 17:25:58 +02:00
|
|
|
veths,
|
2012-07-13 12:49:24 +01:00
|
|
|
ttyFDs, nttyFDs,
|
2015-08-20 19:16:17 +05:30
|
|
|
nsInheritFDs,
|
2013-07-09 18:15:45 +01:00
|
|
|
files, nfiles,
|
2015-01-16 15:03:16 +00:00
|
|
|
handshakefds[1],
|
2021-04-20 13:28:20 +02:00
|
|
|
handshakefds[2],
|
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 17:21:28 +02:00
|
|
|
&logfd,
|
2015-01-16 15:03:16 +00:00
|
|
|
pidfile)))
|
2012-07-13 12:39:29 +01: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 17:53:07 +01:00
|
|
|
g_autofree char *xml = virDomainDefFormat(vm->def, driver->xmlopt, 0);
|
2012-07-13 12:39:29 +01:00
|
|
|
|
|
|
|
/*
|
|
|
|
* If the script raised an error abort the launch
|
|
|
|
*/
|
2020-05-12 17:53:07 +01: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 12:39:29 +01:00
|
|
|
goto cleanup;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Log timestamp */
|
2013-07-04 12:11:37 +02:00
|
|
|
if ((timestamp = virTimeStringNow()) == NULL)
|
2012-07-13 12:39:29 +01: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 18:57:34 +01:00
|
|
|
g_strerror(errno));
|
2012-07-13 12:39:29 +01: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 18:57:34 +01:00
|
|
|
g_strerror(errno));
|
2012-07-13 12:39:29 +01:00
|
|
|
|
2015-02-12 14:32:39 -05:00
|
|
|
VIR_DEBUG("Launching container");
|
2014-02-19 17:32:19 -07:00
|
|
|
virCommandRawStatus(cmd);
|
2013-10-11 17:30:53 +01:00
|
|
|
if (virCommandRun(cmd, &status) < 0)
|
2012-07-13 12:39:29 +01:00
|
|
|
goto cleanup;
|
|
|
|
|
2013-10-11 17:30:53 +01:00
|
|
|
if (status != 0) {
|
2014-02-19 17:32:19 -07:00
|
|
|
if (virLXCProcessReadLogOutput(vm, logfile, pos, ebuf,
|
|
|
|
sizeof(ebuf)) <= 0) {
|
|
|
|
if (WIFEXITED(status))
|
2019-11-13 14:53:42 +01:00
|
|
|
g_snprintf(ebuf, sizeof(ebuf), _("unexpected exit status %d"),
|
|
|
|
WEXITSTATUS(status));
|
2014-02-19 17:32:19 -07:00
|
|
|
else
|
2019-11-13 14:53:42 +01:00
|
|
|
g_snprintf(ebuf, sizeof(ebuf), "%s", _("terminated abnormally"));
|
2014-02-19 17:32:19 -07:00
|
|
|
}
|
2013-10-11 17:30:53 +01: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 17:30:53 +01: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 17:30:53 +01: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 12:39:29 +01:00
|
|
|
goto cleanup;
|
|
|
|
}
|
|
|
|
|
2012-07-17 15:54:08 +01:00
|
|
|
priv->stopReason = VIR_DOMAIN_EVENT_STOPPED_FAILED;
|
2012-07-23 12:25:37 +01:00
|
|
|
priv->wantReboot = false;
|
2012-07-13 12:39:29 +01:00
|
|
|
vm->def->id = vm->pid;
|
|
|
|
virDomainObjSetState(vm, VIR_DOMAIN_RUNNING, reason);
|
2012-07-17 11:55:38 +01:00
|
|
|
priv->doneStopEvent = false;
|
2012-07-13 12:39:29 +01:00
|
|
|
|
2021-04-20 13:28:20 +02:00
|
|
|
if (VIR_CLOSE(handshakefds[1]) < 0 ||
|
|
|
|
VIR_CLOSE(handshakefds[2]) < 0) {
|
2015-01-16 15:03:16 +00:00
|
|
|
virReportSystemError(errno, "%s", _("could not close handshake fd"));
|
2015-02-04 21:42:56 +08:00
|
|
|
goto cleanup;
|
2015-01-16 16:39:57 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
if (virCommandHandshakeWait(cmd) < 0)
|
2015-02-04 21:42:56 +08: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 21:42:56 +08: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 21:42:56 +08:00
|
|
|
goto cleanup;
|
2015-01-16 16:39:57 +00:00
|
|
|
|
2020-01-31 17:04:24 +01: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);
|
|
|
|
|
2021-04-20 10:33:03 +02:00
|
|
|
/* The first synchronization point is when the controller creates CGroups. */
|
2012-07-13 12:39:29 +01:00
|
|
|
if (lxcContainerWaitForContinue(handshakefds[0]) < 0) {
|
|
|
|
char out[1024];
|
|
|
|
|
2012-07-13 12:49:24 +01:00
|
|
|
if (!(virLXCProcessReadLogOutput(vm, logfile, pos, out, 1024) < 0)) {
|
2012-07-13 13:59:51 +01:00
|
|
|
virReportError(VIR_ERR_INTERNAL_ERROR,
|
|
|
|
_("guest failed to start: %s"), out);
|
2012-07-13 12:39:29 +01:00
|
|
|
}
|
|
|
|
|
2015-02-04 21:42:56 +08:00
|
|
|
goto cleanup;
|
2012-07-13 12:39:29 +01:00
|
|
|
}
|
|
|
|
|
2017-07-21 15:51:03 +02: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 16:50:54 +01:00
|
|
|
if (virCgroupNewDetectMachine(vm->def->name, "lxc",
|
2017-07-21 15:51:03 +02:00
|
|
|
vm->pid, -1, priv->machineName,
|
|
|
|
&priv->cgroup) < 0)
|
2015-02-04 21:42:56 +08: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 21:42:56 +08:00
|
|
|
goto cleanup;
|
2015-01-16 15:03:16 +00:00
|
|
|
}
|
|
|
|
|
2021-04-20 10:33:03 +02:00
|
|
|
if (lxcContainerSendContinue(handshakefds[3]) < 0) {
|
|
|
|
virReportSystemError(errno, "%s",
|
|
|
|
_("Failed to send continue signal to controller"));
|
|
|
|
goto cleanup;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* The second synchronization point is when the controller finished
|
|
|
|
* creating the container. */
|
|
|
|
if (lxcContainerWaitForContinue(handshakefds[0]) < 0) {
|
|
|
|
char out[1024];
|
|
|
|
|
|
|
|
if (!(virLXCProcessReadLogOutput(vm, logfile, pos, out, 1024) < 0)) {
|
|
|
|
virReportError(VIR_ERR_INTERNAL_ERROR,
|
|
|
|
_("guest failed to start: %s"), out);
|
|
|
|
}
|
|
|
|
|
|
|
|
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 21:42:56 +08:00
|
|
|
goto cleanup;
|
2015-01-16 15:03:16 +00:00
|
|
|
}
|
|
|
|
|
2012-07-13 12:39:29 +01:00
|
|
|
if (autoDestroy &&
|
2013-07-15 19:08:11 +02:00
|
|
|
virCloseCallbacksSet(driver->closeCallbacks, vm,
|
|
|
|
conn, lxcProcessAutoDestroy) < 0)
|
2015-02-04 21:42:56 +08:00
|
|
|
goto cleanup;
|
2012-07-13 12:39:29 +01:00
|
|
|
|
2014-06-27 10:41:22 +02:00
|
|
|
/* We don't need the temporary NIC names anymore, clear them */
|
|
|
|
virLXCProcessCleanInterfaces(vm->def);
|
|
|
|
|
2012-07-13 12:39:29 +01:00
|
|
|
/* finally we can call the 'started' hook script if any */
|
|
|
|
if (virHookPresent(VIR_HOOK_DRIVER_LXC)) {
|
2020-05-12 17:53:07 +01:00
|
|
|
g_autofree char *xml = virDomainDefFormat(vm->def, driver->xmlopt, 0);
|
2012-07-13 12:39:29 +01:00
|
|
|
|
|
|
|
/*
|
|
|
|
* If the script raised an error abort the launch
|
|
|
|
*/
|
2020-05-12 17:53:07 +01: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 21:42:56 +08:00
|
|
|
goto cleanup;
|
2012-07-13 12:39:29 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
rc = 0;
|
|
|
|
|
2014-03-25 07:49:26 +01:00
|
|
|
cleanup:
|
2012-07-13 12:39:29 +01:00
|
|
|
if (VIR_CLOSE(logfd) < 0) {
|
|
|
|
virReportSystemError(errno, "%s", _("could not close logfile"));
|
|
|
|
rc = -1;
|
|
|
|
}
|
|
|
|
if (rc != 0) {
|
2018-12-06 12:32:37 -05:00
|
|
|
virErrorPreserveLast(&err);
|
2020-11-11 13:51:21 +01:00
|
|
|
if (virDomainObjIsActive(vm)) {
|
|
|
|
virLXCProcessStop(driver, vm, VIR_DOMAIN_SHUTOFF_FAILED);
|
|
|
|
} else {
|
|
|
|
/* virLXCProcessStop() is NOP if the container is not active.
|
|
|
|
* If there was a failure whilst creating it, cleanup manually. */
|
|
|
|
virLXCProcessCleanup(driver, vm, VIR_DOMAIN_SHUTOFF_FAILED, stopFlags);
|
|
|
|
}
|
2012-07-13 12:39:29 +01:00
|
|
|
}
|
2013-05-21 16:03:33 +08:00
|
|
|
for (i = 0; i < nttyFDs; i++)
|
2012-07-13 12:39:29 +01:00
|
|
|
VIR_FORCE_CLOSE(ttyFDs[i]);
|
2021-04-20 13:28:20 +02:00
|
|
|
for (i = 0; i < G_N_ELEMENTS(handshakefds); i++)
|
|
|
|
VIR_FORCE_CLOSE(handshakefds[i]);
|
2012-07-13 12:39:29 +01:00
|
|
|
|
2018-12-06 12:32:37 -05:00
|
|
|
virErrorRestore(&err);
|
2012-07-13 12:39:29 +01:00
|
|
|
|
|
|
|
return rc;
|
|
|
|
}
|
|
|
|
|
2012-07-13 12:49:24 +01:00
|
|
|
struct virLXCProcessAutostartData {
|
2021-03-11 08:16:13 +01:00
|
|
|
virLXCDriver *driver;
|
2012-07-13 12:39:29 +01:00
|
|
|
virConnectPtr conn;
|
|
|
|
};
|
|
|
|
|
2013-01-11 13:54:15 +00:00
|
|
|
static int
|
2021-03-11 08:16:13 +01:00
|
|
|
virLXCProcessAutostartDomain(virDomainObj *vm,
|
2013-01-11 13:54:15 +00:00
|
|
|
void *opaque)
|
2012-07-13 12:39:29 +01:00
|
|
|
{
|
2012-07-13 12:49:24 +01:00
|
|
|
const struct virLXCProcessAutostartData *data = opaque;
|
2013-01-11 13:54:15 +00:00
|
|
|
int ret = 0;
|
2012-07-13 12:39:29 +01:00
|
|
|
|
2013-01-09 21:00:32 +00:00
|
|
|
virObjectLock(vm);
|
2012-07-13 12:39:29 +01:00
|
|
|
if (vm->autostart &&
|
|
|
|
!virDomainObjIsActive(vm)) {
|
2013-07-09 18:15:45 +01: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 12:39:29 +01:00
|
|
|
virDomainAuditStart(vm, "booted", ret >= 0);
|
|
|
|
if (ret < 0) {
|
|
|
|
VIR_ERROR(_("Failed to autostart VM '%s': %s"),
|
|
|
|
vm->def->name,
|
2016-05-19 21:10:19 +02:00
|
|
|
virGetLastErrorMessage());
|
2012-07-13 12:39:29 +01:00
|
|
|
} else {
|
2021-03-11 08:16:13 +01:00
|
|
|
virObjectEvent *event =
|
2013-11-21 18:03:26 +01:00
|
|
|
virDomainEventLifecycleNewFromObj(vm,
|
2012-07-13 12:39:29 +01:00
|
|
|
VIR_DOMAIN_EVENT_STARTED,
|
|
|
|
VIR_DOMAIN_EVENT_STARTED_BOOTED);
|
2018-06-11 15:38:17 -04:00
|
|
|
virObjectEventStateQueue(data->driver->domainEventState, event);
|
2012-07-13 12:39:29 +01:00
|
|
|
}
|
|
|
|
}
|
2013-01-09 21:00:32 +00:00
|
|
|
virObjectUnlock(vm);
|
2013-01-11 13:54:15 +00:00
|
|
|
return ret;
|
2012-07-13 12:39:29 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void
|
2021-03-11 08:16:13 +01:00
|
|
|
virLXCProcessAutostartAll(virLXCDriver *driver)
|
2012-07-13 12:56:29 +01:00
|
|
|
{
|
2012-07-13 12:39:29 +01: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 14:32:07 +01:00
|
|
|
virConnectPtr conn = virConnectOpen("lxc:///system");
|
2012-07-13 12:39:29 +01:00
|
|
|
/* Ignoring NULL conn which is mostly harmless here */
|
|
|
|
|
2012-07-13 12:49:24 +01:00
|
|
|
struct virLXCProcessAutostartData data = { driver, conn };
|
2012-07-13 12:39:29 +01:00
|
|
|
|
2019-09-06 13:59:59 +02:00
|
|
|
virDomainObjListForEach(driver->domains, false,
|
2013-01-11 13:54:15 +00:00
|
|
|
virLXCProcessAutostartDomain,
|
|
|
|
&data);
|
2012-07-13 12:39:29 +01:00
|
|
|
|
2014-01-03 08:08:52 -07:00
|
|
|
virObjectUnref(conn);
|
2012-07-13 12:39:29 +01:00
|
|
|
}
|
|
|
|
|
2019-02-01 17:11:22 +00:00
|
|
|
|
|
|
|
static void
|
2021-03-11 08:16:13 +01:00
|
|
|
virLXCProcessReconnectNotifyNets(virDomainDef *def)
|
2019-02-01 17:11:22 +00:00
|
|
|
{
|
|
|
|
size_t i;
|
2021-01-08 00:31:05 -05:00
|
|
|
g_autoptr(virConnect) conn = NULL;
|
2019-02-01 17:11:22 +00:00
|
|
|
|
|
|
|
for (i = 0; i < def->nnets; i++) {
|
2021-03-11 08:16:13 +01:00
|
|
|
virDomainNetDef *net = def->nets[i];
|
2019-02-01 17:11:22 +00:00
|
|
|
|
2020-12-16 14:58:21 -05:00
|
|
|
/* type='bridge|network|ethernet' interfaces may be using an
|
|
|
|
* autogenerated netdev name, so we should update the counter
|
|
|
|
* for autogenerated names to skip past this one.
|
|
|
|
*/
|
|
|
|
switch (virDomainNetGetActualType(net)) {
|
|
|
|
case VIR_DOMAIN_NET_TYPE_BRIDGE:
|
|
|
|
case VIR_DOMAIN_NET_TYPE_NETWORK:
|
|
|
|
case VIR_DOMAIN_NET_TYPE_ETHERNET:
|
|
|
|
virNetDevReserveName(net->ifname);
|
|
|
|
break;
|
|
|
|
case VIR_DOMAIN_NET_TYPE_DIRECT:
|
|
|
|
case VIR_DOMAIN_NET_TYPE_USER:
|
|
|
|
case VIR_DOMAIN_NET_TYPE_VHOSTUSER:
|
|
|
|
case VIR_DOMAIN_NET_TYPE_SERVER:
|
|
|
|
case VIR_DOMAIN_NET_TYPE_CLIENT:
|
|
|
|
case VIR_DOMAIN_NET_TYPE_MCAST:
|
|
|
|
case VIR_DOMAIN_NET_TYPE_INTERNAL:
|
|
|
|
case VIR_DOMAIN_NET_TYPE_HOSTDEV:
|
|
|
|
case VIR_DOMAIN_NET_TYPE_UDP:
|
|
|
|
case VIR_DOMAIN_NET_TYPE_VDPA:
|
|
|
|
case VIR_DOMAIN_NET_TYPE_LAST:
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
2021-01-08 00:36:31 -05:00
|
|
|
if (net->type == VIR_DOMAIN_NET_TYPE_NETWORK && !conn)
|
|
|
|
conn = virGetConnectNetwork();
|
|
|
|
|
|
|
|
virDomainNetNotifyActualDevice(conn, def, net);
|
2019-02-01 17:11:22 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2013-01-11 13:54:15 +00:00
|
|
|
static int
|
2021-03-11 08:16:13 +01:00
|
|
|
virLXCProcessReconnectDomain(virDomainObj *vm,
|
2013-01-11 13:54:15 +00:00
|
|
|
void *opaque)
|
2012-07-13 12:39:29 +01:00
|
|
|
{
|
2021-03-11 08:16:13 +01:00
|
|
|
virLXCDriver *driver = opaque;
|
|
|
|
virLXCDomainObjPrivate *priv;
|
2021-12-10 14:56:46 +01:00
|
|
|
g_autoptr(virLXCDriverConfig) cfg = virLXCDriverGetConfig(driver);
|
2013-01-11 13:54:15 +00:00
|
|
|
int ret = -1;
|
2012-07-13 12:39:29 +01: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 12:39:29 +01: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 17:04:24 +01: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 11:55:38 +01:00
|
|
|
if (!(priv->monitor = virLXCProcessConnectMonitor(driver, vm)))
|
2012-07-13 12:39:29 +01:00
|
|
|
goto error;
|
|
|
|
|
2017-07-21 15:51:03 +02: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 17:36:42 +01:00
|
|
|
if (!priv->cgroup) {
|
2013-07-22 13:59:28 +01:00
|
|
|
virReportError(VIR_ERR_INTERNAL_ERROR,
|
2013-07-24 17:36:42 +01:00
|
|
|
_("No valid cgroup for machine %s"),
|
2013-07-22 13:59:28 +01: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 12:39:29 +01: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 12:39:29 +01:00
|
|
|
/* now that we know it's reconnected call the hook if present */
|
|
|
|
if (virHookPresent(VIR_HOOK_DRIVER_LXC)) {
|
2020-05-12 17:53:07 +01:00
|
|
|
g_autofree char *xml = virDomainDefFormat(vm->def, driver->xmlopt, 0);
|
2012-07-13 12:39:29 +01:00
|
|
|
|
|
|
|
/* we can't stop the operation even if the script raised an error */
|
2020-05-12 17:53:07 +01: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 12:39:29 +01:00
|
|
|
goto error;
|
|
|
|
}
|
|
|
|
|
|
|
|
} else {
|
|
|
|
vm->def->id = -1;
|
|
|
|
}
|
|
|
|
|
2013-01-11 13:54:15 +00:00
|
|
|
ret = 0;
|
2014-03-25 07:49:26 +01:00
|
|
|
cleanup:
|
2013-01-09 21:00:32 +00:00
|
|
|
virObjectUnlock(vm);
|
2013-01-11 13:54:15 +00:00
|
|
|
return ret;
|
2012-07-13 12:39:29 +01:00
|
|
|
|
2014-03-25 07:49:26 +01:00
|
|
|
error:
|
2012-07-13 12:49:24 +01:00
|
|
|
virLXCProcessStop(driver, vm, VIR_DOMAIN_SHUTOFF_FAILED);
|
2012-07-13 12:39:29 +01:00
|
|
|
virDomainAuditStop(vm, "failed");
|
|
|
|
goto cleanup;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2021-03-11 08:16:13 +01:00
|
|
|
int virLXCProcessReconnectAll(virLXCDriver *driver,
|
|
|
|
virDomainObjList *doms)
|
2012-07-13 12:39:29 +01:00
|
|
|
{
|
2019-09-06 13:59:59 +02:00
|
|
|
virDomainObjListForEach(doms, false, virLXCProcessReconnectDomain, driver);
|
2012-07-13 12:39:29 +01:00
|
|
|
return 0;
|
|
|
|
}
|