From: Zbigniew Jędrzejewski-Szmek Date: Fri, 17 Apr 2026 07:07:31 +0000 (+0200) Subject: ssh-issue: replace verb options by normal verbs X-Git-Url: http://git.ipfire.org/gitweb.cgi?a=commitdiff_plain;h=refs%2Fpull%2F41668%2Fhead;p=thirdparty%2Fsystemd.git ssh-issue: replace verb options by normal verbs 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.) --- diff --git a/man/systemd-ssh-issue.xml b/man/systemd-ssh-issue.xml index 4e887796764..f71a50f755d 100644 --- a/man/systemd-ssh-issue.xml +++ b/man/systemd-ssh-issue.xml @@ -23,15 +23,22 @@ - /usr/lib/systemd/systemd-ssh-issue - /usr/lib/systemd/systemd-ssh-issue + /usr/lib/systemd/systemd-ssh-issue + OPTIONS + make-vsock + + + /usr/lib/systemd/systemd-ssh-issue + OPTIONS + rm-vsock Description - systemd-ssh-issue is a small tool that generates a + systemd-ssh-issue is a small tool that generates (when called with + make-vsock) and removes (when called with rm-vsock) a /run/issue.d/50-ssh-vsock.issue drop-in file in case AF_VSOCK 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-AF_VSOCK, in particular it reports the @@ -49,21 +56,6 @@ The following options are understood: - - - Generates the issue file. This command has no effect if called on systems lacking - AF_VSOCK support. - - - - - - - Removes the issue file if it exists. - - - - Changes the path to the issue file to write to/remove. If not specified, defaults to diff --git a/src/ssh-generator/ssh-generator.c b/src/ssh-generator/ssh-generator.c index 3923710b5cc..bc01250a55b 100644 --- a/src/ssh-generator/ssh-generator.c +++ b/src/ssh-generator/ssh-generator.c @@ -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; diff --git a/src/ssh-generator/ssh-issue.c b/src/ssh-generator/ssh-issue.c index d4e2443d1d9..f1000d60684 100644 --- a/src/ssh-generator/ssh-issue.c +++ b/src/ssh-generator/ssh-issue.c @@ -17,38 +17,134 @@ #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);