]> git.ipfire.org Git - thirdparty/systemd.git/commitdiff
nspawn: introduce --cleanup option to clear propagation and unix-export directories
authorYu Watanabe <watanabe.yu+github@gmail.com>
Tue, 15 Oct 2024 08:25:09 +0000 (17:25 +0900)
committerYu Watanabe <watanabe.yu+github@gmail.com>
Sun, 16 Mar 2025 02:02:09 +0000 (11:02 +0900)
This is useful when the previous invocation is unexpectedly killed.

Otherwise, if systemd-nspawn is killed forcibly, then unix-export
directory is not cleared and unmounted, and the subsequent invocation
will fail. E.g.
===
[   18.895515] TEST-13-NSPAWN.sh[645]: + machinectl start long-running
[   18.945703] systemd-nspawn[1387]: Mount point '/run/systemd/nspawn/unix-export/long-running' exists already, refusing.
[   18.949236] systemd[1]: systemd-nspawn@long-running.service: Failed with result 'exit-code'.
[   18.949743] systemd[1]: Failed to start systemd-nspawn@long-running.service.
===

man/systemd-nspawn.xml
shell-completion/bash/systemd-nspawn
shell-completion/zsh/_systemd-nspawn
src/nspawn/nspawn.c
units/systemd-nspawn@.service.in

index 213f434fe77021b24e0181c341c473fe962cea78..1ade5483a8fa72b6853209636622feeed5910634 100644 (file)
         <xi:include href="version-info.xml" xpointer="v226"/></listitem>
       </varlistentry>
 
+      <varlistentry>
+        <term><option>--cleanup</option></term>
+
+        <listitem>
+          <para>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
+          <command>systemd-nspawn</command> was unexpectedly terminated. This requires at least one of
+          <option>-M/--machine=</option>, <option>-D/--directory=</option>, or <option>-i/--image=</option>
+          to determine the mounts to be cleaned up.</para>
+
+          <xi:include href="version-info.xml" xpointer="v257"/>
+        </listitem>
+      </varlistentry>
+
     </variablelist>
 
     <refsect2>
index e1829287f456169c51fef828adb04d05c27d3754..5d54fca3346af4004265adb7dae91a663411511c 100644 (file)
@@ -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
index a9856b500831cab4ae9b7b9199756f7fb27986ca..5f081700644c6735d20ca8fde98462dc0b58c1ff 100644 (file)
@@ -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.]' \
index 1916e441d8f3e3bb9fe6a7069afd297e7d64c6c4..f19014016f85cfe2898c1c949dda593ba7a4938b 100644 (file)
@@ -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);
 
index 0dec0e04785424da0ffb418154ff5082beee2789..c1426e673bdf9281d37c47bb86fd1da1929ce8fa 100644 (file)
@@ -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