diff options
author | Mike Yuan <me@yhndnzj.com> | 2023-12-19 08:38:32 +0100 |
---|---|---|
committer | Mike Yuan <me@yhndnzj.com> | 2023-12-20 05:46:46 +0100 |
commit | 28bb29cbb48072952bbc47bbf91f1d4d24538766 (patch) | |
tree | d2fa909711509f15b6fd7d77016167a2baeeca64 | |
parent | networkctl-config-file: check for masked config before editing/showing (diff) | |
download | systemd-28bb29cbb48072952bbc47bbf91f1d4d24538766.tar.xz systemd-28bb29cbb48072952bbc47bbf91f1d4d24538766.zip |
networkctl: introduce verb mask and unmask
Suggested in https://github.com/systemd/systemd/pull/29928#discussion_r1386626565
-rw-r--r-- | man/networkctl.xml | 42 | ||||
-rw-r--r-- | src/network/networkctl-config-file.c | 108 | ||||
-rw-r--r-- | src/network/networkctl-config-file.h | 3 | ||||
-rw-r--r-- | src/network/networkctl.c | 4 | ||||
-rwxr-xr-x | test/units/testsuite-74.networkctl.sh | 19 |
5 files changed, 172 insertions, 4 deletions
diff --git a/man/networkctl.xml b/man/networkctl.xml index 3a2dc09ecc..1a03e9e11d 100644 --- a/man/networkctl.xml +++ b/man/networkctl.xml @@ -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> diff --git a/src/network/networkctl-config-file.c b/src/network/networkctl-config-file.c index b212358cfa..670f1c2fd7 100644 --- a/src/network/networkctl-config-file.c +++ b/src/network/networkctl-config-file.c @@ -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); +} diff --git a/src/network/networkctl-config-file.h b/src/network/networkctl-config-file.h index 8d7069e3f3..38210a8093 100644 --- a/src/network/networkctl-config-file.h +++ b/src/network/networkctl-config-file.h @@ -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); diff --git a/src/network/networkctl.c b/src/network/networkctl.c index aaa2b3a430..cf9d17c8b2 100644 --- a/src/network/networkctl.c +++ b/src/network/networkctl.c @@ -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 }, {} }; diff --git a/test/units/testsuite-74.networkctl.sh b/test/units/testsuite-74.networkctl.sh index b857abcf9a..06a3c39e77 100755 --- a/test/units/testsuite-74.networkctl.sh +++ b/test/units/testsuite-74.networkctl.sh @@ -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 |