From: Zbigniew Jędrzejewski-Szmek Date: Fri, 27 Feb 2026 10:38:29 +0000 (+0100) Subject: verbs: add a section to list verbs similarly to options X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=d03089ff4f445437022cf312d4062e6a9a98dce2;p=thirdparty%2Fsystemd.git verbs: add a section to list verbs similarly to options --- diff --git a/src/shared/verbs.c b/src/shared/verbs.c index 8c174a6c88d..bf2d440bb0e 100644 --- a/src/shared/verbs.c +++ b/src/shared/verbs.c @@ -3,6 +3,7 @@ #include #include "env-util.h" +#include "format-table.h" #include "log.h" #include "string-util.h" #include "strv.h" @@ -57,33 +58,34 @@ bool should_bypass(const char *env_prefix) { return true; } -const Verb* verbs_find_verb(const char *name, const Verb verbs[]) { +const Verb* verbs_find_verb(const char *name, const Verb verbs[], const Verb verbs_end[]) { assert(verbs); - for (size_t i = 0; verbs[i].dispatch; i++) - if (name ? streq(name, verbs[i].verb) : FLAGS_SET(verbs[i].flags, VERB_DEFAULT)) - return verbs + i; + for (const Verb *verb = verbs; verb < verbs_end; verb++) + if (name ? streq(name, verb->verb) : FLAGS_SET(verb->flags, VERB_DEFAULT)) + return verb; /* At the end of the list? */ return NULL; } -int dispatch_verb_with_args(char **args, const Verb verbs[], void *userdata) { +int _dispatch_verb_with_args(char **args, const Verb verbs[], const Verb verbs_end[], void *userdata) { int r; assert(verbs); + assert(verbs_end > verbs); assert(verbs[0].dispatch); assert(verbs[0].verb); const char *name = args ? args[0] : NULL; size_t left = strv_length(args); - const Verb *verb = verbs_find_verb(name, verbs); + const Verb *verb = verbs_find_verb(name, verbs, verbs_end); if (!verb) { _cleanup_strv_free_ char **verb_strv = NULL; - for (size_t i = 0; verbs[i].dispatch; i++) { - r = strv_extend(&verb_strv, verbs[i].verb); + for (verb = verbs; verb < verbs_end; verb++) { + r = strv_extend(&verb_strv, verb->verb); if (r < 0) return log_oom(); } @@ -139,5 +141,46 @@ int dispatch_verb(int argc, char *argv[], const Verb verbs[], void *userdata) { assert(argv); assert(argc >= optind); - return dispatch_verb_with_args(strv_skip(argv, optind), verbs, userdata); + size_t n = 0; + while (verbs[n].dispatch) + n++; + + return _dispatch_verb_with_args(strv_skip(argv, optind), verbs, verbs + n, userdata); +} + +int _verbs_get_help_table(const Verb verbs[], const Verb verbs_end[], Table **ret) { + int r; + + assert(ret); + + _cleanup_(table_unrefp) Table *table = table_new("verb", "help"); + if (!table) + return log_oom(); + + for (const Verb *verb = verbs; verb < verbs_end; verb++) { + assert(verb->dispatch); + + /* We indent the option string by two spaces. We could set the minimum cell width and + * right-align for a similar result, but that'd be more work. This is only used for + * display. */ + r = table_add_cell_stringf(table, NULL, " %s%s%s", + verb->verb, + verb->argspec ? " " : "", + strempty(verb->argspec)); + if (r < 0) + return table_log_add_error(r); + + const char *help = verb->help ?: "FIXME"; + _cleanup_strv_free_ char **s = strv_split(help, /* separators= */ NULL); + if (!s) + return log_oom(); + + r = table_add_many(table, TABLE_STRV_WRAPPED, s); + if (r < 0) + return table_log_add_error(r); + } + + table_set_header(table, false); + *ret = TAKE_PTR(table); + return 0; } diff --git a/src/shared/verbs.h b/src/shared/verbs.h index febb8ccfc24..062e49466f9 100644 --- a/src/shared/verbs.h +++ b/src/shared/verbs.h @@ -16,12 +16,58 @@ typedef struct { VerbFlags flags; int (* const dispatch)(int argc, char *argv[], uintptr_t data, void *userdata); uintptr_t data; + const char *argspec; + const char *help; } Verb; +#define VERB_FULL(d, v, a, amin, amax, f, dat, h) \ + static int d(int, char**, uintptr_t, void*); \ + _section_("SYSTEMD_VERBS") \ + _alignptr_ \ + _used_ \ + _retain_ \ + _variable_no_sanitize_address_ \ + static const Verb CONCATENATE(d, _data) = { \ + .verb = v, \ + .min_args = amin, \ + .max_args = amax, \ + .flags = f, \ + .dispatch = d, \ + .data = dat, \ + .argspec = a, \ + .help = h, \ + } + +/* The same as VERB_FULL, but without the data argument */ +#define VERB(d, v, a, amin, amax, f, h) \ + VERB_FULL(d, v, a, amin, amax, f, /* dat= */ 0, h) + +/* Simplified VERB for parameters that take no argument */ +#define VERB_NOARG(d, v, h) \ + VERB(d, v, /* a= */ NULL, /* amin= */ VERB_ANY, /* amax= */ 1, /* f= */ 0, h) + +/* This is magically mapped to the beginning and end of the section */ +extern const Verb __start_SYSTEMD_VERBS[]; +extern const Verb __stop_SYSTEMD_VERBS[]; + bool running_in_chroot_or_offline(void); bool should_bypass(const char *env_prefix); -const Verb* verbs_find_verb(const char *name, const Verb verbs[]); -int dispatch_verb_with_args(char **args, const Verb verbs[], void *userdata); +const Verb* verbs_find_verb(const char *name, const Verb verbs[], const Verb verbs_end[]); + +int _dispatch_verb_with_args(char **args, const Verb verbs[], const Verb verbs_end[], void *userdata); +#define dispatch_verb_with_args(args, userdata) \ + _dispatch_verb_with_args(args, ALIGN_PTR(__start_SYSTEMD_VERBS), __stop_SYSTEMD_VERBS, userdata) + int dispatch_verb(int argc, char *argv[], const Verb verbs[], void *userdata); + +int _verbs_get_help_table(const Verb verbs[], const Verb verbs_end[], Table **ret); +#define verbs_get_help_table(ret) \ + _verbs_get_help_table(ALIGN_PTR(__start_SYSTEMD_VERBS), __stop_SYSTEMD_VERBS, ret) + +#define VERB_COMMON_HELP(impl) \ + VERB(verb_help, "help", NULL, VERB_ANY, VERB_ANY, 0, "Show this help"); \ + static int verb_help(int argc, char **argv, uintptr_t data, void *userdata) { \ + return impl(); \ + } diff --git a/src/systemctl/systemctl-main.c b/src/systemctl/systemctl-main.c index 565b3a8878a..4d1830071f4 100644 --- a/src/systemctl/systemctl-main.c +++ b/src/systemctl/systemctl-main.c @@ -135,7 +135,7 @@ static int systemctl_main(int argc, char *argv[]) { {} }; - const Verb *verb = verbs_find_verb(argv[optind], verbs); + const Verb *verb = verbs_find_verb(argv[optind], verbs, verbs + ELEMENTSOF(verbs) - 1); if (verb && (verb->flags & VERB_ONLINE_ONLY) && arg_root) return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Verb '%s' cannot be used with --root= or --image=.",