]> git.ipfire.org Git - thirdparty/systemd.git/commitdiff
networkctl: support edit --stdin 33979/head
authorMike Yuan <me@yhndnzj.com>
Fri, 5 Jul 2024 19:34:07 +0000 (21:34 +0200)
committerMike Yuan <me@yhndnzj.com>
Mon, 12 Aug 2024 14:23:23 +0000 (16:23 +0200)
man/networkctl.xml
src/network/networkctl-config-file.c
src/network/networkctl.c
src/network/networkctl.h
src/systemctl/systemctl-edit.c
src/systemctl/systemctl.c
test/units/TEST-74-AUX-UTILS.networkctl.sh

index 5e2126ff218c7ea773f938f7e59f35901559c71f..e47cf5895cf8648f4492a1a6c493fc5e6138804d 100644 (file)
@@ -448,6 +448,9 @@ s - Service VLAN, m - Two-port MAC Relay (TPMR)
         <citerefentry><refentrytitle>systemd.link</refentrytitle><manvolnum>5</manvolnum></citerefentry>
         for more information.</para>
 
+        <para>If <option>--stdin</option> is specified, the new content will be read from standard input.
+        In this mode, the old content of the file is discarded.</para>
+
         <xi:include href="version-info.xml" xpointer="v254"/></listitem>
       </varlistentry>
 
@@ -608,6 +611,23 @@ s - Service VLAN, m - Two-port MAC Relay (TPMR)
         </listitem>
       </varlistentry>
 
+      <varlistentry>
+        <term><option>--stdin</option></term>
+
+        <listitem>
+          <para>When used with <command>edit</command>, the contents of the file will be read from standard
+          input and the editor will not be launched. In this mode, the old contents of the file are
+          automatically replaced. This is useful to "edit" configuration from scripts, especially so that
+          drop-in directories are created and populated in one go.</para>
+
+          <para>Multiple drop-ins may be "edited" in this mode with <option>--drop-in=</option>, and
+          the same contents will be written to all of them. Otherwise exactly one main configuration file
+          is expected.</para>
+
+          <xi:include href="version-info.xml" xpointer="v257"/>
+        </listitem>
+      </varlistentry>
+
       <xi:include href="standard-options.xml" xpointer="json" />
       <xi:include href="standard-options.xml" xpointer="help" />
       <xi:include href="standard-options.xml" xpointer="version" />
index 216e9d49543ffe45791ac7cb2a8cd37466bc9fc8..1aa03619b728f8c6d4f23fa3c5edae08a7f2e5d2 100644 (file)
@@ -396,23 +396,30 @@ static int reload_daemons(ReloadFlags flags) {
 }
 
 int verb_edit(int argc, char *argv[], void *userdata) {
+        char **args = ASSERT_PTR(strv_skip(argv, 1));
         _cleanup_(edit_file_context_done) EditFileContext context = {
                 .marker_start = DROPIN_MARKER_START,
                 .marker_end = DROPIN_MARKER_END,
                 .remove_parent = !!arg_drop_in,
+                .stdin = arg_stdin,
         };
         _cleanup_(sd_netlink_unrefp) sd_netlink *rtnl = NULL;
         ReloadFlags reload = 0;
         int r;
 
-        if (!on_tty())
-                return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Cannot edit network config files if not on a tty.");
+        if (!on_tty() && !arg_stdin)
+                return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Cannot edit network config files interactively if not on a tty.");
+
+        /* Duplicating main configs makes no sense. This also mimics the behavior of systemctl. */
+        if (arg_stdin && !arg_drop_in && strv_length(args) != 1)
+                return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
+                                       "When 'edit --stdin' without '--drop-in=', exactly one config file for editing must be specified.");
 
         r = mac_selinux_init();
         if (r < 0)
                 return r;
 
-        STRV_FOREACH(name, strv_skip(argv, 1)) {
+        STRV_FOREACH(name, args) {
                 _cleanup_strv_free_ char **dropins = NULL;
                 _cleanup_free_ char *path = NULL;
                 const char *link_config;
index b51c7aa7e596b66e93ed63f5ebf5e58b6e7545db..6c96a84c46bdd5313b2d287aee337642eedfb9e4 100644 (file)
@@ -91,6 +91,7 @@ bool arg_all = false;
 bool arg_stats = false;
 bool arg_full = false;
 bool arg_runtime = false;
+bool arg_stdin = false;
 unsigned arg_lines = 10;
 char *arg_drop_in = NULL;
 sd_json_format_flags_t arg_json_format_flags = SD_JSON_FORMAT_OFF;
@@ -3025,6 +3026,7 @@ static int help(void) {
                "                         after editing network config\n"
                "     --drop-in=NAME      Edit specified drop-in instead of main config file\n"
                "     --runtime           Edit runtime config files\n"
+               "     --stdin             Read new contents of edited file from stdin\n"
                "\nSee the %s for details.\n",
                program_invocation_short_name,
                ansi_highlight(),
@@ -3043,6 +3045,7 @@ static int parse_argv(int argc, char *argv[]) {
                 ARG_NO_RELOAD,
                 ARG_DROP_IN,
                 ARG_RUNTIME,
+                ARG_STDIN,
         };
 
         static const struct option options[] = {
@@ -3058,6 +3061,7 @@ static int parse_argv(int argc, char *argv[]) {
                 { "no-reload", no_argument,       NULL, ARG_NO_RELOAD },
                 { "drop-in",   required_argument, NULL, ARG_DROP_IN   },
                 { "runtime",   no_argument,       NULL, ARG_RUNTIME   },
+                { "stdin",     no_argument,       NULL, ARG_STDIN     },
                 {}
         };
 
@@ -3092,6 +3096,10 @@ static int parse_argv(int argc, char *argv[]) {
                         arg_runtime = true;
                         break;
 
+                case ARG_STDIN:
+                        arg_stdin = true;
+                        break;
+
                 case ARG_DROP_IN:
                         if (isempty(optarg))
                                 return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Empty drop-in file name.");
index 1765a8f83df7e0292e3012ea888b012f62df89f5..d44ee8173e01fcb9c188278d9d1b64434ce7e0db 100644 (file)
@@ -15,6 +15,7 @@ extern bool arg_all;
 extern bool arg_stats;
 extern bool arg_full;
 extern bool arg_runtime;
+extern bool arg_stdin;
 extern unsigned arg_lines;
 extern char *arg_drop_in;
 extern sd_json_format_flags_t arg_json_format_flags;
index 15398f83646eb8d30bac92ec450df463de9ed0ae..b7bd3851fd0b9576b63b4b7e21f4aae652add34c 100644 (file)
@@ -323,7 +323,7 @@ int verb_edit(int argc, char *argv[], void *userdata) {
         int r;
 
         if (!on_tty() && !arg_stdin)
-                return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Cannot edit units if not on a tty.");
+                return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Cannot edit units interactively if not on a tty.");
 
         if (arg_transport != BUS_TRANSPORT_LOCAL)
                 return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Cannot edit units remotely.");
index 1e36455cf21e1ade8abe18907c20564b32515198..912c1d83017f4b64e7167a37ad20c1a10bf54f26 100644 (file)
@@ -347,7 +347,7 @@ static int systemctl_help(void) {
                "     --drop-in=NAME      Edit unit files using the specified drop-in file name\n"
                "     --when=TIME         Schedule halt/power-off/reboot/kexec action after\n"
                "                         a certain timestamp\n"
-               "     --stdin             Read contents of edited file from stdin\n"
+               "     --stdin             Read new contents of edited file from stdin\n"
                "\nSee the %2$s for details.\n",
                program_invocation_short_name,
                link,
index 3e333a2eabe8bb72361142a83ed5e9adb4e890d7..8c62de9155a0c02ee096459afbef50bc7671a24c 100755 (executable)
@@ -11,7 +11,7 @@ at_exit() {
     systemctl stop systemd-networkd
 
     if [[ -v NETWORK_NAME && -v NETDEV_NAME && -v LINK_NAME ]]; then
-        rm -fvr {/usr/lib,/etc,/run}/systemd/network/"$NETWORK_NAME" "/usr/lib/systemd/network/$NETDEV_NAME" \
+        rm -fvr {/usr/lib,/etc,/run}/systemd/network/"$NETWORK_NAME" "/run/lib/systemd/network/$NETDEV_NAME" \
             {/usr/lib,/etc}/systemd/network/"$LINK_NAME" "/etc/systemd/network/${NETWORK_NAME}.d" \
             "new" "+4"
     fi
@@ -75,13 +75,14 @@ cmp "+4" "/etc/systemd/network/${NETWORK_NAME}.d/test.conf"
 networkctl cat "$NETWORK_NAME" | grep '^# ' |
     cmp - <(printf '%s\n' "# /etc/systemd/network/$NETWORK_NAME" "# /etc/systemd/network/${NETWORK_NAME}.d/test.conf")
 
-cat >"/usr/lib/systemd/network/$NETDEV_NAME" <<EOF
+networkctl edit --stdin --runtime "$NETDEV_NAME" <<EOF
 [NetDev]
 Name=test2
 Kind=dummy
 EOF
 
-networkctl cat "$NETDEV_NAME"
+networkctl cat "$NETDEV_NAME" | grep -v '^# ' |
+    cmp - <(printf '%s\n' "[NetDev]" "Name=test2" "Kind=dummy")
 
 cat >"/usr/lib/systemd/network/$LINK_NAME" <<EOF
 [Match]