]> git.ipfire.org Git - thirdparty/systemd.git/commitdiff
ssh-issue: replace verb options by normal verbs 41668/head
authorZbigniew Jędrzejewski-Szmek <zbyszek@amutable.com>
Fri, 17 Apr 2026 07:07:31 +0000 (09:07 +0200)
committerZbigniew Jędrzejewski-Szmek <zbyszek@amutable.com>
Fri, 17 Apr 2026 09:54:47 +0000 (11:54 +0200)
The interface of this program was rather strange. It took an option that
specified what to do, but that option behaved exactly like a verb. Let's
change the interface to the more modern style with verbs. Since the
inteface was documented in the man page, provide a compat shim to handle
the old options.

(In practice, I doubt anybody will notice the change. But since it was
documented, it's easier to provide the compat then to think too much
whether it is actually needed. I think we can drop it an year or so.)

man/systemd-ssh-issue.xml
src/ssh-generator/ssh-generator.c
src/ssh-generator/ssh-issue.c

index 4e887796764e0585964f9f1ca71f1e0e196d93f5..f71a50f755d73b68d6f9f1ebf49961e2f4a121a3 100644 (file)
 
   <refsynopsisdiv>
     <cmdsynopsis>
-      <command>/usr/lib/systemd/systemd-ssh-issue <option>--make-vsock</option></command>
-      <command>/usr/lib/systemd/systemd-ssh-issue <option>--rm-vsock</option></command>
+      <command>/usr/lib/systemd/systemd-ssh-issue</command>
+      <arg choice="opt" rep="repeat">OPTIONS</arg>
+      <arg choice="plain">make-vsock</arg>
+    </cmdsynopsis>
+    <cmdsynopsis>
+      <command>/usr/lib/systemd/systemd-ssh-issue</command>
+      <arg choice="opt" rep="repeat">OPTIONS</arg>
+      <arg choice="plain">rm-vsock</arg>
     </cmdsynopsis>
   </refsynopsisdiv>
 
   <refsect1>
     <title>Description</title>
 
-    <para><command>systemd-ssh-issue</command> is a small tool that generates a
+    <para><command>systemd-ssh-issue</command> is a small tool that generates (when called with
+    <command>make-vsock</command>) and removes (when called with <command>rm-vsock</command>) a
     <filename>/run/issue.d/50-ssh-vsock.issue</filename> drop-in file in case <constant>AF_VSOCK</constant>
     support is available in the kernel and the VM environment. The file contains brief information about how
     to contact the local system via SSH-over-<constant>AF_VSOCK</constant>, in particular it reports the
     <para>The following options are understood:</para>
 
     <variablelist>
-      <varlistentry>
-        <term><option>--make-vsock</option></term>
-        <listitem><para>Generates the issue file. This command has no effect if called on systems lacking
-        <constant>AF_VSOCK</constant> support.</para>
-
-        <xi:include href="version-info.xml" xpointer="v258"/></listitem>
-      </varlistentry>
-
-      <varlistentry>
-        <term><option>--rm-vsock</option></term>
-        <listitem><para>Removes the issue file if it exists.</para>
-
-        <xi:include href="version-info.xml" xpointer="v258"/></listitem>
-      </varlistentry>
-
       <varlistentry>
         <term><option>--issue-path=</option></term>
         <listitem><para>Changes the path to the issue file to write to/remove. If not specified, defaults to
index 3923710b5cca07cecae53bfe8a58b0fbbcb767a8..bc01250a55b123ffde4b90c141674a9a82f135bc 100644 (file)
@@ -238,8 +238,8 @@ static int add_vsock_socket(
                         "sshd-vsock.socket",
                         "vsock::22",
                         "AF_VSOCK",
-                        "ExecStartPost=-/usr/lib/systemd/systemd-ssh-issue --make-vsock\n"
-                        "ExecStopPre=-/usr/lib/systemd/systemd-ssh-issue --rm-vsock\n",
+                        "ExecStartPost=-/usr/lib/systemd/systemd-ssh-issue make-vsock\n"
+                        "ExecStopPre=-/usr/lib/systemd/systemd-ssh-issue rm-vsock\n",
                         /* with_ssh_access_target_dependency= */ true);
         if (r < 0)
                 return r;
index d4e2443d1d91dfe4276a8420c3207e810077c17e..f1000d60684a95215efb8be278e7ecd560d7aded 100644 (file)
 #include "pretty-print.h"
 #include "ssh-util.h"
 #include "string-util.h"
+#include "strv.h"
 #include "tmpfile-util.h"
+#include "verbs.h"
 #include "virt.h"
 
-static enum {
-        ACTION_MAKE_VSOCK,
-        ACTION_RM_VSOCK,
-} arg_action = ACTION_MAKE_VSOCK;
-
 static char *arg_issue_path = NULL;
 static bool arg_issue_stdout = false;
 
 STATIC_DESTRUCTOR_REGISTER(arg_issue_path, freep);
 
+static int acquire_cid(unsigned *ret_cid) {
+        int r;
+
+        assert(ret_cid);
+
+        Virtualization v = detect_virtualization();
+        if (v < 0)
+                return log_error_errno(v, "Failed to detect if we run in a VM: %m");
+        if (!VIRTUALIZATION_IS_VM(v)) {
+                /* NB: if we are running in a container inside a VM, then we'll *not* do AF_VSOCK stuff */
+                log_debug("Not running in a VM, not creating issue file.");
+                *ret_cid = 0;
+                return 0;
+        }
+
+        r = vsock_open_or_warn(/* ret= */ NULL);
+        if (r <= 0)
+                return r;
+
+        return vsock_get_local_cid_or_warn(ret_cid);
+}
+
+VERB_NOARG(verb_make_vsock, "make-vsock",
+           "Generate the issue file");
+static int verb_make_vsock(int argc, char *argv[], uintptr_t _data, void *_userdata) {
+        unsigned cid;
+        int r;
+
+        r = acquire_cid(&cid);
+        if (r < 0)
+                return r;
+        if (r == 0) {
+                log_debug("Not running in a VSOCK enabled VM, skipping.");
+                return 0;
+        }
+
+        _cleanup_(unlink_and_freep) char *t = NULL;
+        _cleanup_(fclosep) FILE *f = NULL;
+        FILE *out;
+
+        if (arg_issue_path)  {
+                r = mkdir_parents(arg_issue_path, 0755);
+                if (r < 0)
+                        return log_error_errno(r, "Failed to create parent directories of '%s': %m", arg_issue_path);
+
+                r = fopen_tmpfile_linkable(arg_issue_path, O_WRONLY|O_CLOEXEC, &t, &f);
+                if (r < 0)
+                        return log_error_errno(r, "Failed to create '%s': %m", arg_issue_path);
+
+                out = f;
+        } else
+                out = stdout;
+
+        fprintf(out,
+                "Try contacting this VM's SSH server via 'ssh vsock%%%u' from host.\n"
+                "\n", cid);
+
+        if (f) {
+                if (fchmod(fileno(f), 0644) < 0)
+                        return log_error_errno(errno, "Failed to adjust access mode of '%s': %m", arg_issue_path);
+
+                r = flink_tmpfile(f, t, arg_issue_path, LINK_TMPFILE_REPLACE);
+                if (r < 0)
+                        return log_error_errno(r, "Failed to move '%s' into place: %m", arg_issue_path);
+        }
+
+        return 0;
+}
+
+VERB_NOARG(verb_rm_vsock, "rm-vsock",
+           "Remove the issue file");
+static int verb_rm_vsock(int argc, char *argv[], uintptr_t _data, void *_userdata) {
+        if (arg_issue_path) {
+                if (unlink(arg_issue_path) < 0) {
+                        if (errno != ENOENT)
+                                return log_error_errno(errno, "Failed to remove '%s': %m", arg_issue_path);
+
+                        log_debug_errno(errno, "File '%s' does not exist, no operation executed.", arg_issue_path);
+                } else
+                        log_debug("Successfully removed '%s'.", arg_issue_path);
+        } else
+                log_notice("STDOUT selected for issue file, not removing.");
+
+        return 0;
+}
+
 static int help(void) {
         _cleanup_free_ char *link = NULL;
-        _cleanup_(table_unrefp) Table *options = NULL;
+        _cleanup_(table_unrefp) Table *options = NULL, *verbs = NULL;
         int r;
 
         r = terminal_urlify_man("systemd-ssh-issue", "1", &link);
         if (r < 0)
                 return log_oom();
 
+        r = verbs_get_help_table(&verbs);
+        if (r < 0)
+                return r;
+
         r = option_parser_get_help_table(&options);
         if (r < 0)
                 return r;
 
-        printf("%1$s [OPTIONS...] --make-vsock\n"
-               "%1$s [OPTIONS...] --rm-vsock\n"
-               "\n%2$sCreate ssh /run/issue.d/ file reporting VSOCK address.%3$s\n\n",
+        (void) table_sync_column_widths(0, verbs, options);
+
+        printf("%s [OPTIONS...] COMMAND\n"
+               "\n%sCreate/remove ssh /run/issue.d/ file reporting VSOCK address.%s\n"
+               "\n%sCommands:%s\n",
                program_invocation_short_name,
-               ansi_highlight(),
-               ansi_normal());
+               ansi_highlight(), ansi_normal(),
+               ansi_underline(), ansi_normal());
+
+        r = table_print_or_warn(verbs);
+        if (r < 0)
+                return r;
+
+        printf("\n%sOptions:%s\n",
+               ansi_underline(), ansi_normal());
 
         r = table_print_or_warn(options);
         if (r < 0)
@@ -58,15 +154,17 @@ static int help(void) {
         return 0;
 }
 
-static int parse_argv(int argc, char *argv[]) {
+static int parse_argv(int argc, char *argv[], char ***ret_args) {
         assert(argc >= 0);
         assert(argv);
+        assert(ret_args);
 
         OptionParser state = { argc, argv };
-        const char *arg;
+        const Option *opt;
+        const char *arg, *verb = NULL;
         int r;
 
-        FOREACH_OPTION(&state, c, &arg, /* on_error= */ return c)
+        FOREACH_OPTION_FULL(&state, c, &opt, &arg, /* on_error= */ return c)
                 switch (c) {
 
                 OPTION_COMMON_HELP:
@@ -75,12 +173,9 @@ static int parse_argv(int argc, char *argv[]) {
                 OPTION_COMMON_VERSION:
                         return version();
 
-                OPTION_LONG("make-vsock", NULL, "Generate the issue file (default)"):
-                        arg_action = ACTION_MAKE_VSOCK;
-                        break;
-
-                OPTION_LONG("rm-vsock", NULL, "Remove the issue file"):
-                        arg_action = ACTION_RM_VSOCK;
+                OPTION_LONG("make-vsock", NULL, /* help= */ NULL): {}
+                OPTION_LONG("rm-vsock", NULL, /* help= */ NULL):
+                        verb = opt->long_code;
                         break;
 
                 OPTION_LONG("issue-path", "PATH",
@@ -105,104 +200,32 @@ static int parse_argv(int argc, char *argv[]) {
                         return log_oom();
         }
 
-        return 1;
-}
-
-static int acquire_cid(unsigned *ret_cid) {
-        int r;
-
-        assert(ret_cid);
-
-        Virtualization v = detect_virtualization();
-        if (v < 0)
-                return log_error_errno(v, "Failed to detect if we run in a VM: %m");
-        if (!VIRTUALIZATION_IS_VM(v)) {
-                /* NB: if we are running in a container inside a VM, then we'll *not* do AF_VSOCK stuff */
-                log_debug("Not running in a VM, not creating issue file.");
-                *ret_cid = 0;
-                return 0;
-        }
-
-        r = vsock_open_or_warn(/* ret= */ NULL);
-        if (r <= 0)
-                return r;
+        char **args;
+        if (verb) {
+                if (option_parser_get_n_args(&state) > 0)
+                        return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Invalid use of compat option --make-vsock/--rm-vsock.");
+                log_warning("Options --make-vsock/--rm-vsock have been replaced by make-vsock/rm-vsock verbs.");
+                args = strv_new(verb);
+        } else
+                args = strv_copy(option_parser_get_args(&state));
+        if (!args)
+                return log_oom();
 
-        return vsock_get_local_cid_or_warn(ret_cid);
+        *ret_args = args;
+        return 1;
 }
 
 static int run(int argc, char* argv[]) {
+        _cleanup_strv_free_ char **args = NULL;
         int r;
 
         log_setup();
 
-        r = parse_argv(argc, argv);
+        r = parse_argv(argc, argv, &args);
         if (r <= 0)
                 return r;
 
-        switch (arg_action) {
-        case ACTION_MAKE_VSOCK: {
-                unsigned cid;
-
-                r = acquire_cid(&cid);
-                if (r < 0)
-                        return r;
-                if (r == 0) {
-                        log_debug("Not running in a VSOCK enabled VM, skipping.");
-                        break;
-                }
-
-                _cleanup_(unlink_and_freep) char *t = NULL;
-                _cleanup_(fclosep) FILE *f = NULL;
-                FILE *out;
-
-                if (arg_issue_path)  {
-                        r = mkdir_parents(arg_issue_path, 0755);
-                        if (r < 0)
-                                return log_error_errno(r, "Failed to create parent directories of '%s': %m", arg_issue_path);
-
-                        r = fopen_tmpfile_linkable(arg_issue_path, O_WRONLY|O_CLOEXEC, &t, &f);
-                        if (r < 0)
-                                return log_error_errno(r, "Failed to create '%s': %m", arg_issue_path);
-
-                        out = f;
-                } else
-                        out = stdout;
-
-                fprintf(out,
-                        "Try contacting this VM's SSH server via 'ssh vsock%%%u' from host.\n"
-                        "\n", cid);
-
-                if (f) {
-                        if (fchmod(fileno(f), 0644) < 0)
-                                return log_error_errno(errno, "Failed to adjust access mode of '%s': %m", arg_issue_path);
-
-                        r = flink_tmpfile(f, t, arg_issue_path, LINK_TMPFILE_REPLACE);
-                        if (r < 0)
-                                return log_error_errno(r, "Failed to move '%s' into place: %m", arg_issue_path);
-                }
-
-                break;
-        }
-
-        case ACTION_RM_VSOCK:
-                if (arg_issue_path) {
-                        if (unlink(arg_issue_path) < 0) {
-                                if (errno != ENOENT)
-                                        return log_error_errno(errno, "Failed to remove '%s': %m", arg_issue_path);
-
-                                log_debug_errno(errno, "File '%s' does not exist, no operation executed.", arg_issue_path);
-                        } else
-                                log_debug("Successfully removed '%s'.", arg_issue_path);
-                } else
-                        log_notice("STDOUT selected for issue file, not removing.");
-
-                break;
-
-        default:
-                assert_not_reached();
-        }
-
-        return 0;
+        return dispatch_verb_with_args(args, NULL);
 }
 
 DEFINE_MAIN_FUNCTION(run);