diff --git a/man/systemd-nspawn.xml b/man/systemd-nspawn.xml
index 213f434fe7..1ade5483a8 100644
--- a/man/systemd-nspawn.xml
+++ b/man/systemd-nspawn.xml
@@ -223,6 +223,20 @@
+
+
+
+
+ Clean up left-over mounts and underlying mount points used by the container, and exit without
+ invoking any containers. This may be useful when the previous invocation of
+ systemd-nspawn was unexpectedly terminated. This requires at least one of
+ , , or
+ to determine the mounts to be cleaned up.
+
+
+
+
+
diff --git a/shell-completion/bash/systemd-nspawn b/shell-completion/bash/systemd-nspawn
index e1829287f4..5d54fca334 100644
--- a/shell-completion/bash/systemd-nspawn
+++ b/shell-completion/bash/systemd-nspawn
@@ -66,7 +66,8 @@ _systemd_nspawn() {
local -A OPTS=(
[STANDALONE]='-h --help --version --private-network -b --boot --read-only -q --quiet --share-system
- --keep-unit -n --network-veth -j -x --ephemeral -a --as-pid2 -U --suppress-sync=yes'
+ --keep-unit -n --network-veth -j -x --ephemeral -a --as-pid2 -U --suppress-sync=yes
+ --cleanup'
[ARG]='-D --directory -u --user --uuid --capability --drop-capability --link-journal --bind --bind-ro
-M --machine -S --slice -E --setenv -Z --selinux-context -L --selinux-apifs-context
--register --network-interface --network-bridge --personality -i --image --image-policy --tmpfs
diff --git a/shell-completion/zsh/_systemd-nspawn b/shell-completion/zsh/_systemd-nspawn
index a9856b5008..5f08170064 100644
--- a/shell-completion/zsh/_systemd-nspawn
+++ b/shell-completion/zsh/_systemd-nspawn
@@ -16,6 +16,7 @@ _arguments \
'(- *)'{-h,--help}'[Show this help.]' \
'(- *)--version[Print a short version string and exit.]' \
'(--quiet -q)'{--quiet,-q}'[Turns off any status output by the tool itself.]' \
+ '--cleanup[Cleanup left-over mounts and underlying mount points used by the container.]' \
'(--directory -D)'{--directory=,-D+}'[Directory to use as file system root for the namespace container. If omitted the current directory will be used.]:directories:_directories' \
'--template=[Initialize root directory from template directory, if missing.]:template:_directories' \
'(--ephemeral -x)'{--ephemeral,-x}'[Run container with snapshot of root directory, and remove it after exit.]' \
diff --git a/src/nspawn/nspawn.c b/src/nspawn/nspawn.c
index 1916e441d8..f19014016f 100644
--- a/src/nspawn/nspawn.c
+++ b/src/nspawn/nspawn.c
@@ -236,6 +236,7 @@ static Architecture arg_architecture = _ARCHITECTURE_INVALID;
static ImagePolicy *arg_image_policy = NULL;
static char *arg_background = NULL;
static bool arg_privileged = false;
+static bool arg_cleanup = false;
STATIC_DESTRUCTOR_REGISTER(arg_directory, freep);
STATIC_DESTRUCTOR_REGISTER(arg_template, freep);
@@ -327,6 +328,8 @@ static int help(void) {
" -q --quiet Do not show status information\n"
" --no-pager Do not pipe output into a pager\n"
" --settings=BOOLEAN Load additional settings from .nspawn file\n"
+ " --cleanup Clean up left-over mounts and underlying mount\n"
+ " points used by the container\n"
"\n%3$sImage:%4$s\n"
" -D --directory=PATH Root directory for the container\n"
" --template=PATH Initialize root directory from template directory,\n"
@@ -751,6 +754,7 @@ static int parse_argv(int argc, char *argv[]) {
ARG_SUPPRESS_SYNC,
ARG_IMAGE_POLICY,
ARG_BACKGROUND,
+ ARG_CLEANUP,
};
static const struct option options[] = {
@@ -826,6 +830,7 @@ static int parse_argv(int argc, char *argv[]) {
{ "suppress-sync", required_argument, NULL, ARG_SUPPRESS_SYNC },
{ "image-policy", required_argument, NULL, ARG_IMAGE_POLICY },
{ "background", required_argument, NULL, ARG_BACKGROUND },
+ { "cleanup", no_argument, NULL, ARG_CLEANUP },
{}
};
@@ -1609,6 +1614,10 @@ static int parse_argv(int argc, char *argv[]) {
return r;
break;
+ case ARG_CLEANUP:
+ arg_cleanup = true;
+ break;
+
case '?':
return -EINVAL;
@@ -5919,6 +5928,34 @@ static void initialize_defaults(void) {
arg_private_network = !arg_privileged;
}
+static void cleanup_propagation_and_export_directories(void) {
+ const char *p;
+
+ if (!arg_machine || !arg_privileged)
+ return;
+
+ p = strjoina("/run/systemd/nspawn/propagate/", arg_machine);
+ (void) rm_rf(p, REMOVE_ROOT);
+
+ p = strjoina("/run/systemd/nspawn/unix-export/", arg_machine);
+ (void) umount2(p, MNT_DETACH|UMOUNT_NOFOLLOW);
+ (void) rmdir(p);
+}
+
+static int do_cleanup(void) {
+ int r;
+
+ if (arg_ephemeral)
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Cannot specify --ephemeral with --cleanup.");
+
+ r = determine_names();
+ if (r < 0)
+ return r;
+
+ cleanup_propagation_and_export_directories();
+ return 0;
+}
+
static int run(int argc, char *argv[]) {
bool remove_directory = false, remove_image = false, veth_created = false;
_cleanup_close_ int master = -EBADF, userns_fd = -EBADF, mount_fd = -EBADF;
@@ -5941,6 +5978,9 @@ static int run(int argc, char *argv[]) {
if (r <= 0)
goto finish;
+ if (arg_cleanup)
+ return do_cleanup();
+
r = cant_be_in_netns();
if (r < 0)
goto finish;
@@ -6476,6 +6516,8 @@ finish:
(void) rmdir(p);
}
+ cleanup_propagation_and_export_directories();
+
expose_port_flush(&fw_ctx, arg_expose_ports, AF_INET, &expose_args.address4);
expose_port_flush(&fw_ctx, arg_expose_ports, AF_INET6, &expose_args.address6);
diff --git a/units/systemd-nspawn@.service.in b/units/systemd-nspawn@.service.in
index 0dec0e0478..c1426e673b 100644
--- a/units/systemd-nspawn@.service.in
+++ b/units/systemd-nspawn@.service.in
@@ -19,6 +19,7 @@ RequiresMountsFor=/var/lib/machines/%i
[Service]
# Make sure the DeviceAllow= lines below can properly resolve the 'block-loop' expression (and others)
ExecStart=systemd-nspawn --quiet --keep-unit --boot --link-journal=try-guest --network-veth -U --settings=override --machine=%i
+ExecStopPost=systemd-nspawn --cleanup --machine=%i
KillMode=mixed
Type=notify
RestartForceExitStatus=133