]> git.ipfire.org Git - thirdparty/systemd.git/commitdiff
networkctl: introduce verb mask and unmask 30525/head
authorMike Yuan <me@yhndnzj.com>
Tue, 19 Dec 2023 07:38:32 +0000 (15:38 +0800)
committerMike Yuan <me@yhndnzj.com>
Wed, 20 Dec 2023 04:46:46 +0000 (12:46 +0800)
Suggested in https://github.com/systemd/systemd/pull/29928#discussion_r1386626565

man/networkctl.xml
src/network/networkctl-config-file.c
src/network/networkctl-config-file.h
src/network/networkctl.c
test/units/testsuite-74.networkctl.sh

index 3a2dc09eccbe6b28c8aa6a37b801feb35c419504..1a03e9e11d445da9d3440b04dda1f0557f220547 100644 (file)
@@ -461,6 +461,40 @@ s - Service VLAN, m - Two-port MAC Relay (TPMR)
 
         <xi:include href="version-info.xml" xpointer="v254"/></listitem>
       </varlistentry>
+
+      <varlistentry>
+        <term>
+          <command>mask</command>
+          <replaceable>FILE</replaceable>…
+        </term>
+        <listitem><para>Mask network configuration files, which include <filename>.network</filename>,
+        <filename>.netdev</filename>, and <filename>.link</filename> files. A symlink of the given name will
+        be created under <filename>/etc/</filename> or <filename>/run/</filename>, depending on
+        whether <option>--runtime</option> is specified, that points to <filename>/dev/null</filename>.
+        If a non-empty config file with the specified name exists under the target directory or a directory
+        with higher priority (e.g. <option>--runtime</option> is used while an existing config resides
+        in <filename>/etc/</filename>), the operation is aborted.</para>
+
+        <para>This command honors <option>--no-reload</option> in the same way as <command>edit</command>.
+        </para>
+
+        <xi:include href="version-info.xml" xpointer="v256"/></listitem>
+      </varlistentry>
+
+      <varlistentry>
+        <term>
+          <command>unmask</command>
+          <replaceable>FILE</replaceable>…
+        </term>
+        <listitem><para>Unmask network configuration files, i.e. reverting the effect of <command>mask</command>.
+        Note that this command operates regardless of the scope of the directory, i.e. <option>--runtime</option>
+        is of no effect.</para>
+
+        <para>This command honors <option>--no-reload</option> in the same way as <command>edit</command>
+        and <command>mask</command>.</para>
+
+        <xi:include href="version-info.xml" xpointer="v256"/></listitem>
+      </varlistentry>
     </variablelist>
   </refsect1>
 
@@ -534,11 +568,11 @@ s - Service VLAN, m - Two-port MAC Relay (TPMR)
         <term><option>--no-reload</option></term>
 
         <listitem>
-          <para>When used with <command>edit</command>,
+          <para>When used with <command>edit</command>, <command>mask</command>, or <command>unmask</command>,
           <citerefentry><refentrytitle>systemd-networkd.service</refentrytitle><manvolnum>8</manvolnum></citerefentry>
           or
           <citerefentry><refentrytitle>systemd-udevd.service</refentrytitle><manvolnum>8</manvolnum></citerefentry>
-          will not be reloaded after the editing finishes.</para>
+          will not be reloaded after the operation finishes.</para>
 
         <xi:include href="version-info.xml" xpointer="v254"/>
         </listitem>
@@ -547,8 +581,8 @@ s - Service VLAN, m - Two-port MAC Relay (TPMR)
         <term><option>--runtime</option></term>
 
         <listitem>
-          <para>When used with <command>edit</command>, edit the file under <filename>/run/</filename>
-          instead of <filename>/etc/</filename>.</para>
+          <para>When used with <command>edit</command> or <command>mask</command>,
+          operate on the file under <filename>/run/</filename> instead of <filename>/etc/</filename>.</para>
 
           <xi:include href="version-info.xml" xpointer="v256"/>
         </listitem>
index b212358cfaac3f8123b78a265e7921c5099837ef..670f1c2fd7b6cd250569ad23adb78431c3500f51 100644 (file)
@@ -13,6 +13,7 @@
 #include "bus-wait-for-jobs.h"
 #include "conf-files.h"
 #include "edit-util.h"
+#include "mkdir-label.h"
 #include "netlink-util.h"
 #include "networkctl.h"
 #include "networkctl-config-file.h"
@@ -518,3 +519,110 @@ int verb_cat(int argc, char *argv[], void *userdata) {
 
         return ret;
 }
+
+int verb_mask(int argc, char *argv[], void *userdata) {
+        ReloadFlags flags = 0;
+        int r;
+
+        r = mac_selinux_init();
+        if (r < 0)
+                return r;
+
+        STRV_FOREACH(name, strv_skip(argv, 1)) {
+                _cleanup_free_ char *config_path = NULL, *symlink_path = NULL;
+                ReloadFlags reload;
+
+                /* We update the real 'flags' at last, since the operation can be skipped. */
+                if (ENDSWITH_SET(*name, ".network", ".netdev"))
+                        reload = RELOAD_NETWORKD;
+                else if (endswith(*name, ".link"))
+                        reload = RELOAD_UDEVD;
+                else
+                        return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Invalid network config name '%s'.", *name);
+
+                r = get_config_files_by_name(*name, /* allow_masked = */ true, &config_path, /* ret_dropins = */ NULL);
+                if (r == -ENOENT)
+                        log_warning("No existing network config '%s' found, proceeding anyway.", *name);
+                else if (r < 0)
+                        return log_error_errno(r, "Failed to get the path of network config '%s': %m", *name);
+                else if (!path_startswith(config_path, "/usr")) {
+                        r = null_or_empty_path(config_path);
+                        if (r < 0)
+                                return log_error_errno(r,
+                                                       "Failed to check if '%s' is masked: %m", config_path);
+                        if (r > 0) {
+                                log_debug("%s is already masked, skipping.", config_path);
+                                continue;
+                        }
+
+                        /* At this point, we have found a config under mutable dir (/run/ or /etc/),
+                         * so masking through /run/ (--runtime) is not possible. If it's under /etc/,
+                         * then it doesn't work without --runtime either. */
+                        if (arg_runtime || path_startswith(config_path, "/etc"))
+                                return log_error_errno(SYNTHETIC_ERRNO(EEXIST),
+                                                       "Cannot mask network config %s: %s exists",
+                                                       *name, config_path);
+                }
+
+                symlink_path = path_join(NETWORK_DIRS[arg_runtime ? 1 : 0], *name);
+                if (!symlink_path)
+                        return log_oom();
+
+                (void) mkdir_parents_label(symlink_path, 0755);
+
+                if (symlink("/dev/null", symlink_path) < 0)
+                        return log_error_errno(errno,
+                                               "Failed to create symlink '%s' to /dev/null: %m", symlink_path);
+
+                flags |= reload;
+                log_info("Successfully created symlink '%s' to /dev/null.", symlink_path);
+        }
+
+        return reload_daemons(flags);
+}
+
+int verb_unmask(int argc, char *argv[], void *userdata) {
+        ReloadFlags flags = 0;
+        int r;
+
+        STRV_FOREACH(name, strv_skip(argv, 1)) {
+                _cleanup_free_ char *path = NULL;
+                ReloadFlags reload;
+
+                if (ENDSWITH_SET(*name, ".network", ".netdev"))
+                        reload = RELOAD_NETWORKD;
+                else if (endswith(*name, ".link"))
+                        reload = RELOAD_UDEVD;
+                else
+                        return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Invalid network config name '%s'.", *name);
+
+                r = get_config_files_by_name(*name, /* allow_masked = */ true, &path, /* ret_dropins = */ NULL);
+                if (r == -ENOENT) {
+                        log_debug_errno(r, "Network configuration '%s' doesn't exist, skipping.", *name);
+                        continue;
+                }
+                if (r < 0)
+                        return log_error_errno(r, "Failed to get the path of network config '%s': %m", *name);
+
+                r = null_or_empty_path(path);
+                if (r < 0)
+                        return log_error_errno(r, "Failed to check if '%s' is masked: %m", path);
+                if (r == 0)
+                        continue;
+
+                if (path_startswith(path, "/usr"))
+                        return log_error_errno(r, "Cannot unmask network config under /usr/: %s", path);
+
+                if (unlink(path) < 0) {
+                        if (errno == ENOENT)
+                                continue;
+
+                        return log_error_errno(errno, "Failed to remove '%s': %m", path);
+                }
+
+                flags |= reload;
+                log_info("Successfully removed masked network config '%s'.", path);
+        }
+
+        return reload_daemons(flags);
+}
index 8d7069e3f3171994a7a2b0a5c3d0a47a43c3ce27..38210a8093b323858d808c77ca7d3e833295e9c6 100644 (file)
@@ -3,3 +3,6 @@
 
 int verb_edit(int argc, char *argv[], void *userdata);
 int verb_cat(int argc, char *argv[], void *userdata);
+
+int verb_mask(int argc, char *argv[], void *userdata);
+int verb_unmask(int argc, char *argv[], void *userdata);
index aaa2b3a4303a533b1d6b8f960dce1a761e38d939..cf9d17c8b2fe98c37a760fd3b623125f195126c9 100644 (file)
@@ -2877,6 +2877,8 @@ static int help(void) {
                "  reload                 Reload .network and .netdev files\n"
                "  edit FILES|DEVICES...  Edit network configuration files\n"
                "  cat FILES|DEVICES...   Show network configuration files\n"
+               "  mask FILES...          Mask network configuration files\n"
+               "  unmask FILES...        Unmask network configuration files\n"
                "\nOptions:\n"
                "  -h --help              Show this help\n"
                "     --version           Show package version\n"
@@ -3033,6 +3035,8 @@ static int networkctl_main(int argc, char *argv[]) {
                 { "reload",      1,        1,        VERB_ONLINE_ONLY,              verb_reload         },
                 { "edit",        2,        VERB_ANY, 0,                             verb_edit           },
                 { "cat",         2,        VERB_ANY, 0,                             verb_cat            },
+                { "mask",        2,        VERB_ANY, 0,                             verb_mask           },
+                { "unmask",      2,        VERB_ANY, 0,                             verb_unmask         },
                 {}
         };
 
index b857abcf9a6ebb604ce628c6b9bed34cae0c4340..06a3c39e776a6c60971a3583ffcc619d6a05329f 100755 (executable)
@@ -28,6 +28,16 @@ Name=test
 EOF
 
 # Test files
+
+networkctl mask --runtime "donotexist.network"
+assert_eq "$(readlink /run/systemd/network/donotexist.network)" "/dev/null"
+networkctl unmask "donotexist.network" # unmask should work even without --runtime
+[[ ! -e /run/systemd/network/donotexist.network ]]
+
+touch /usr/lib/systemd/network/donotexist.network
+(! networkctl unmask "donotexist.network")
+rm /usr/lib/systemd/network/donotexist.network
+
 networkctl cat "$NETWORK_NAME" | tail -n +2 | cmp - "/usr/lib/systemd/network/$NETWORK_NAME"
 
 cat >new <<EOF
@@ -36,11 +46,20 @@ Name=test2
 EOF
 
 EDITOR='mv new' script -ec 'networkctl edit --runtime "$NETWORK_NAME"' /dev/null
+(! networkctl mask --runtime "$NETWORK_NAME")
 printf '%s\n' '[Match]' 'Name=test2' | cmp - "/run/systemd/network/$NETWORK_NAME"
 
+networkctl mask "$NETWORK_NAME"
+assert_eq "$(readlink "/etc/systemd/network/$NETWORK_NAME")" "/dev/null"
+(! networkctl edit "$NETWORK_NAME")
+(! networkctl edit --runtime "$NETWORK_NAME")
+(! networkctl cat "$NETWORK_NAME")
+networkctl unmask "$NETWORK_NAME"
+
 EDITOR='true' script -ec 'networkctl edit "$NETWORK_NAME"' /dev/null
 printf '%s\n' '[Match]' 'Name=test2' | cmp - "/etc/systemd/network/$NETWORK_NAME"
 
+(! networkctl mask "$NETWORK_NAME")
 (! EDITOR='true' script -ec 'networkctl edit --runtime "$NETWORK_NAME"' /dev/null)
 
 cat >"+4" <<EOF