From 23e8e4e80f03545a32eee5e1ce1ced4c8ed53b26 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Zbigniew=20J=C4=99drzejewski-Szmek?= Date: Thu, 27 Aug 2026 10:15:52 +0200 Subject: [PATCH 01/21] shared/verbs: rename "footer" to "postscript" in introspection The spec PR uses that term now. I didn't change the internal variable name because I don't like the new name. --- src/shared/verbs.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/shared/verbs.c b/src/shared/verbs.c index 4d4eca9c95..37ceb9fed6 100644 --- a/src/shared/verbs.c +++ b/src/shared/verbs.c @@ -649,7 +649,7 @@ static int command_build_json( "abstract", SD_JSON_BUILD_STRV(STRV_MAKE(cmd->abstract))), SD_JSON_BUILD_PAIR_CONDITION( !!cmd->footer, - "footer", SD_JSON_BUILD_STRV(STRV_MAKE(cmd->footer))), + "postscript", SD_JSON_BUILD_STRV(STRV_MAKE(cmd->footer))), SD_JSON_BUILD_PAIR_CONDITION(!!opts, "options", SD_JSON_BUILD_VARIANT(opts)), SD_JSON_BUILD_PAIR_CONDITION(!!cmds, "verbs", SD_JSON_BUILD_VARIANT(cmds))); if (r < 0) From 057fc4251ac94a05c39d2f01c62425f969fd522a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Zbigniew=20J=C4=99drzejewski-Szmek?= Date: Wed, 26 Aug 2026 10:07:26 +0200 Subject: [PATCH 02/21] shared/verbs: allow verbs to declare an option namespace Verbs like udevadm's subcommands parse their own options from a dedicated option namespace. Allow the namespace to be declared in the verb definition (via the new VERB_SCOPE_NS* macros), and report the options of that namespace in the verb's CLI introspection object. This is conformant with the CLI-Introspection Specification: verbs are represented as nested command objects, which may carry their own "options" array just like the top-level command object. --- src/shared/verbs.c | 20 ++++++++++++++++++-- src/shared/verbs.h | 34 ++++++++++++++++++++++++---------- src/test/test-verbs.c | 32 +++++++++++++++++++++++++++++++- 3 files changed, 73 insertions(+), 13 deletions(-) diff --git a/src/shared/verbs.c b/src/shared/verbs.c index 37ceb9fed6..b9b0c40a76 100644 --- a/src/shared/verbs.c +++ b/src/shared/verbs.c @@ -550,7 +550,13 @@ int _command_print_help_full( return 0; } -static int verb_build_json(const Verb *verb, sd_json_variant **ret) { +static int verb_build_json( + const Verb *verb, + const Option options[], + const Option options_end[], + sd_json_variant **ret) { + + _cleanup_(sd_json_variant_unrefp) sd_json_variant *opts = NULL; int r; assert(verb); @@ -559,17 +565,27 @@ static int verb_build_json(const Verb *verb, sd_json_variant **ret) { /* Verbs are represented as command objects, as described in the CLI-Introspection * Specification (https://uapi-group.org/specifications/specs/cli_introspection/). + * In particular, a verb which declares an option namespace carries its own "options" + * array, just like the top-level command object. * * The "minArguments", "maxArguments", "isDefault", and "isOnlineOnly" fields * are extensions not (yet) covered by the specification. Note that the argument * counts include the verb itself. */ + if (verb->option_namespace) { + r = options_build_json(options, options_end, verb->option_namespace, + /* option_groups= */ NULL, &opts); + if (r < 0) + return r; + } + r = sd_json_buildo( ret, SD_JSON_BUILD_PAIR_STRV("names", STRV_MAKE(verb->verb)), SD_JSON_BUILD_PAIR_CONDITION( !!verb->help, "abstract", SD_JSON_BUILD_STRV(STRV_MAKE(verb->help))), + SD_JSON_BUILD_PAIR_CONDITION(!!opts, "options", SD_JSON_BUILD_VARIANT(opts)), SD_JSON_BUILD_PAIR_CONDITION( verb->min_args != VERB_ANY, "minArguments", SD_JSON_BUILD_UNSIGNED(verb->min_args)), @@ -625,7 +641,7 @@ static int command_build_json( continue; _cleanup_(sd_json_variant_unrefp) sd_json_variant *o = NULL; - r = verb_build_json(verb, &o); + r = verb_build_json(verb, options, options_end, &o); if (r < 0) return r; diff --git a/src/shared/verbs.h b/src/shared/verbs.h index 72b6d70f4f..b8a990a62b 100644 --- a/src/shared/verbs.h +++ b/src/shared/verbs.h @@ -43,10 +43,11 @@ typedef struct _alignptr_ Verb { uintptr_t data; const char *argspec; const char *help; + const char *option_namespace; /* optional namespace with the verb's own options */ } Verb; assert_cc(sizeof(Verb) % sizeof(void*) == 0); -#define _VERB_DATA(d, v, a, amin, amax, f, dat, h) \ +#define _VERB_DATA(d, v, ns, a, amin, amax, f, dat, h) \ _section_("SYSTEMD_VERBS") \ _alignptr_ \ _used_ \ @@ -62,21 +63,33 @@ assert_cc(sizeof(Verb) % sizeof(void*) == 0); .data = dat, \ .argspec = a, \ .help = h, \ + .option_namespace = ns, \ } -/* Forward-define function d. scope specifies the scope, e.g. static. */ -#define VERB_SCOPE_FULL(scope, d, v, a, amin, amax, f, dat, h) \ +/* Forward-define function d. scope specifies the scope, e.g. static. ns declares the option + * namespace with the verb's own options, which are then shown in the verb's help (see + * command_print_verb_help()) and reported in the CLI introspection. */ +#define VERB_SCOPE_NS_FULL(scope, d, v, ns, a, amin, amax, f, dat, h) \ DISABLE_WARNING_REDUNDANT_DECLS \ scope int d(int, char**, uintptr_t, void*); \ REENABLE_WARNING \ - _VERB_DATA(d, v, a, amin, amax, f, dat, h) -/* The same as VERB_SCOPE_FULL with scope hardwired to 'static'. */ + _VERB_DATA(d, v, ns, a, amin, amax, f, dat, h) +/* The same as VERB_SCOPE_NS_FULL, but for verbs without options of their own. */ +#define VERB_SCOPE_FULL(scope, d, v, a, amin, amax, f, dat, h) \ + VERB_SCOPE_NS_FULL(scope, d, v, /* ns= */ NULL, a, amin, amax, f, dat, h) +/* The same as VERB_SCOPE_NS_FULL/VERB_SCOPE_FULL with scope hardwired to 'static'. */ +#define VERB_NS_FULL(d, v, ns, a, amin, amax, f, dat, h) \ + VERB_SCOPE_NS_FULL(static, d, v, ns, a, amin, amax, f, dat, h) #define VERB_FULL(d, v, a, amin, amax, f, dat, h) \ VERB_SCOPE_FULL(static, d, v, a, amin, amax, f, dat, h) -/* The same as VERB_SCOPE_FULL/VERB_FULL, but without the data argument. */ +/* The same as the macros above, but without the data argument. */ +#define VERB_SCOPE_NS(scope, d, v, ns, a, amin, amax, f, h) \ + VERB_SCOPE_NS_FULL(scope, d, v, ns, a, amin, amax, f, /* dat= */ 0, h) #define VERB_SCOPE(scope, d, v, a, amin, amax, f, h) \ VERB_SCOPE_FULL(scope, d, v, a, amin, amax, f, /* dat= */ 0, h) +#define VERB_NS(d, v, ns, a, amin, amax, f, h) \ + VERB_SCOPE_NS(static, d, v, ns, a, amin, amax, f, h) #define VERB(d, v, a, amin, amax, f, h) \ VERB_SCOPE(static, d, v, a, amin, amax, f, h) @@ -91,13 +104,14 @@ assert_cc(sizeof(Verb) % sizeof(void*) == 0); /* Magic entry in the table (which will not be returned) that designates the start of the group . * The macro works as a separator between groups and must be between other VERB* stanzas. */ #define VERB_GROUP(gr) \ - _VERB_DATA(/* d= */ NULL, /* v= */ gr, /* a= */ NULL, /* amin= */ 0, /* amax= */ 0, \ - /* f= */ VERB_GROUP_MARKER, /* dat= */ 0, /* h= */ NULL) + _VERB_DATA(/* d= */ NULL, /* v= */ gr, /* ns= */ NULL, /* a= */ NULL, \ + /* amin= */ 0, /* amax= */ 0, /* f= */ VERB_GROUP_MARKER, /* dat= */ 0, /* h= */ NULL) #define _COMMAND(u, ...) \ static const CommandDescription UNIQ_T(description, u) = { __VA_ARGS__ }; \ - _VERB_DATA(/* d= */ NULL, /* v= */ NULL, /* a= */ NULL, /* amin= */ 0, /* amax= */ 0, \ - /* f= */ VERB_COMMAND_MARKER, /* dat= */ (uintptr_t) &UNIQ_T(description, u), /* h= */ NULL) + _VERB_DATA(/* d= */ NULL, /* v= */ NULL, /* ns= */ NULL, /* a= */ NULL, \ + /* amin= */ 0, /* amax= */ 0, /* f= */ VERB_COMMAND_MARKER, \ + /* dat= */ (uintptr_t) &UNIQ_T(description, u), /* h= */ NULL) #define COMMAND(...) _COMMAND(UNIQ, __VA_ARGS__) /* This is magically mapped to the beginning and end of the section */ diff --git a/src/test/test-verbs.c b/src/test/test-verbs.c index 56062b0bd6..8fc9e2a25c 100644 --- a/src/test/test-verbs.c +++ b/src/test/test-verbs.c @@ -1,5 +1,8 @@ /* SPDX-License-Identifier: LGPL-2.1-or-later */ +#include "sd-json.h" + +#include "options.h" #include "strv.h" #include "tests.h" #include "verbs.h" @@ -9,7 +12,7 @@ static int noop_dispatcher(int argc, char *argv[], uintptr_t _data, void *userda } #define test_dispatch_one(argv, verbs, expected) \ - assert_se(_dispatch_verb(argv, verbs, verbs + ELEMENTSOF(verbs) - 1, NULL) == expected); + ASSERT_EQ(_dispatch_verb(argv, verbs, verbs + ELEMENTSOF(verbs) - 1, NULL), expected); TEST(verbs) { static const Verb verbs[] = { @@ -84,4 +87,31 @@ TEST(verbs_no_default_many) { test_dispatch_one(STRV_MAKE("Specials"), verbs, -EINVAL); } +static const CommandDescription introspect_command = { + .names = "test-verbs\0", + .abstract = "Test the verb machinery.", + .man_pages = "systemd(1)\0", +}; + +static const Verb introspect_verbs[] = { + { .flags = VERB_COMMAND_MARKER, .data = (uintptr_t) &introspect_command }, + { .verb = "alpha", .min_args = VERB_ANY, .max_args = VERB_ANY, .dispatch = noop_dispatcher, + .argspec = "ARG", .help = "Alpha verb", .option_namespace = "test-verbs-alpha" }, + { .verb = "beta", .min_args = VERB_ANY, .max_args = VERB_ANY, .dispatch = noop_dispatcher, + .help = "Beta verb" }, + {} +}; + +static const Option introspect_options[] = { + { 1, .long_code = "test-verbs-alpha", .flags = OPTION_NAMESPACE_MARKER }, + { 2, .short_code = 'x', .long_code = "example", .metavar = "ARG", .help = "An example option" }, + {} +}; + +TEST(introspect_cli_verb_options) { + ASSERT_OK(_introspect_cli(introspect_verbs, introspect_verbs + ELEMENTSOF(introspect_verbs) - 1, + introspect_options, introspect_options + ELEMENTSOF(introspect_options) - 1, + SD_JSON_FORMAT_OFF)); +} + DEFINE_TEST_MAIN(LOG_INFO); From efd63ddfbf265b7116367a86da25e9ae6161f678 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Zbigniew=20J=C4=99drzejewski-Szmek?= Date: Thu, 27 Aug 2026 11:17:10 +0200 Subject: [PATCH 03/21] shared/verbs: turn verb argument specification into nulstr Like the command-level CommandDescription.argspec, a verb's argspec is now a nulstr, so that a verb may declare multiple alternative usage lines. The verbs help table renders one synopsis line per nulstr entry in the synopsis cell. All verb argument specifications must gain an explicit NUL suffix. This is a lot of churn, but I think this needs to be done. For now, there'll be only one user of the new functionality (i.e. a verb with more than one argspec), but I think we'll eventually want to use this in other places too. --- src/analyze/analyze.c | 54 +++++++------- src/backlight/backlight.c | 4 +- src/bootctl/bootctl.c | 22 +++--- src/busctl/busctl.c | 20 ++--- src/clonesetup/clonesetup.c | 4 +- src/coredump/coredumpctl.c | 10 +-- src/creds/creds.c | 6 +- src/cryptsetup/cryptsetup.c | 4 +- src/home/homectl.c | 36 ++++----- src/home/test-homectl-prompts.c | 4 +- src/hostname/hostnamectl.c | 22 +++--- src/hwdb/hwdb.c | 2 +- src/id128/id128.c | 2 +- src/import/export.c | 4 +- src/import/import-fs.c | 2 +- src/import/import.c | 4 +- src/import/importctl.c | 18 ++--- src/import/pull.c | 6 +- src/integritysetup/integritysetup.c | 4 +- src/kernel-install/kernel-install.c | 6 +- src/locale/localectl.c | 8 +- src/login/loginctl.c | 34 ++++----- src/machine/machinectl.c | 58 +++++++------- src/network/networkctl.c | 30 ++++---- src/pcrlock/pcrlock.c | 16 ++-- src/portable/portablectl.c | 16 ++-- src/report/report.c | 8 +- src/resolve/resolvectl.c | 30 ++++---- src/sbsign/sbsign.c | 2 +- src/shared/verbs.c | 66 +++++++++------- src/shared/verbs.h | 4 +- src/storage/storagectl.c | 4 +- src/systemctl/systemctl.c | 112 ++++++++++++++-------------- src/sysupdate/sysupdate.c | 16 ++-- src/sysupdate/updatectl.c | 14 ++-- src/test/test-verbs.c | 2 +- src/timedate/timedatectl.c | 12 +-- src/udev/iocost/iocost.c | 4 +- src/udev/udevadm.c | 18 ++--- src/userdb/userdbctl.c | 10 +-- src/varlinkctl/varlinkctl.c | 14 ++-- src/veritysetup/veritysetup.c | 4 +- 42 files changed, 365 insertions(+), 351 deletions(-) diff --git a/src/analyze/analyze.c b/src/analyze/analyze.c index b8b1119216..a2db0061ec 100644 --- a/src/analyze/analyze.c +++ b/src/analyze/analyze.c @@ -213,19 +213,19 @@ VERB_SCOPE(, verb_time, "time", NULL, VERB_ANY, 1, VERB_DEFAULT, "Print time required to boot the machine"); VERB_SCOPE(, verb_blame, "blame", NULL, VERB_ANY, 1, 0, "Print list of running units ordered by time to init"); -VERB_SCOPE(, verb_critical_chain, "critical-chain", "[UNIT...]", VERB_ANY, VERB_ANY, 0, +VERB_SCOPE(, verb_critical_chain, "critical-chain", "[UNIT...]\0", VERB_ANY, VERB_ANY, 0, "Print a tree of the time critical chain of units"); VERB_GROUP("Dependency Analysis"); VERB_SCOPE(, verb_plot, "plot", NULL, VERB_ANY, 1, 0, "Output SVG graphic showing service initialization"); -VERB_SCOPE(, verb_dot, "dot", "[UNIT...]", VERB_ANY, VERB_ANY, 0, +VERB_SCOPE(, verb_dot, "dot", "[UNIT...]\0", VERB_ANY, VERB_ANY, 0, "Output dependency graph in dot(1) format"); -VERB_SCOPE(, verb_dump, "dump", "[PATTERN...]", VERB_ANY, VERB_ANY, 0, +VERB_SCOPE(, verb_dump, "dump", "[PATTERN...]\0", VERB_ANY, VERB_ANY, 0, "Output state serialization of service manager"); VERB_GROUP("Configuration Files and Search Paths"); -VERB_SCOPE(, verb_cat_config, "cat-config", "NAME|PATH...", 2, VERB_ANY, 0, +VERB_SCOPE(, verb_cat_config, "cat-config", "NAME|PATH...\0", 2, VERB_ANY, 0, "Show configuration file and drop-ins"); VERB_SCOPE(, verb_unit_files, "unit-files", NULL, VERB_ANY, VERB_ANY, 0, "List files and symlinks for units"); @@ -233,57 +233,57 @@ VERB_SCOPE(, verb_unit_paths, "unit-paths", NULL, 1, 1, 0, "List load directories for units"); VERB_GROUP("Enumerate OS Concepts"); -VERB_SCOPE(, verb_exit_status, "exit-status", "[STATUS...]", VERB_ANY, VERB_ANY, 0, +VERB_SCOPE(, verb_exit_status, "exit-status", "[STATUS...]\0", VERB_ANY, VERB_ANY, 0, "List exit status definitions"); -VERB_SCOPE(, verb_capabilities, "capability", "[CAP...]", VERB_ANY, VERB_ANY, 0, +VERB_SCOPE(, verb_capabilities, "capability", "[CAP...]\0", VERB_ANY, VERB_ANY, 0, "List capability definitions"); -VERB_SCOPE(, verb_syscall_filters, "syscall-filter", "[NAME...]", VERB_ANY, VERB_ANY, 0, +VERB_SCOPE(, verb_syscall_filters, "syscall-filter", "[NAME...]\0", VERB_ANY, VERB_ANY, 0, "List syscalls in seccomp filters"); -VERB_SCOPE(, verb_filesystems, "filesystems", "[NAME...]", VERB_ANY, VERB_ANY, 0, +VERB_SCOPE(, verb_filesystems, "filesystems", "[NAME...]\0", VERB_ANY, VERB_ANY, 0, "List known filesystems"); -VERB_SCOPE(, verb_architectures, "architectures", "[NAME...]", VERB_ANY, VERB_ANY, 0, +VERB_SCOPE(, verb_architectures, "architectures", "[NAME...]\0", VERB_ANY, VERB_ANY, 0, "List known architectures"); VERB_SCOPE(, verb_smbios11, "smbios11", NULL, VERB_ANY, 1, 0, "List strings passed via SMBIOS Type #11"); VERB_SCOPE(, verb_chid, "chid", NULL, VERB_ANY, VERB_ANY, 0, "List local CHIDs"); -VERB(verb_transient_settings, "transient-settings", "TYPE...", 2, VERB_ANY, 0, +VERB(verb_transient_settings, "transient-settings", "TYPE...\0", 2, VERB_ANY, 0, "List transient settings for unit TYPE"); VERB_GROUP("Expression Evaluation"); -VERB_SCOPE(, verb_condition, "condition", "CONDITION...", VERB_ANY, VERB_ANY, 0, +VERB_SCOPE(, verb_condition, "condition", "CONDITION...\0", VERB_ANY, VERB_ANY, 0, "Evaluate conditions and asserts"); -VERB_SCOPE(, verb_compare_versions, "compare-versions", "V1 [OP] V2", 3, 4, 0, +VERB_SCOPE(, verb_compare_versions, "compare-versions", "V1 [OP] V2\0", 3, 4, 0, "Compare two version strings"); -VERB_SCOPE(, verb_image_policy, "image-policy", "POLICY...", 2, 2, 0, +VERB_SCOPE(, verb_image_policy, "image-policy", "POLICY...\0", 2, 2, 0, "Analyze image policy string"); VERB_GROUP("Clock & Time"); -VERB_SCOPE(, verb_calendar, "calendar", "SPEC...", 2, VERB_ANY, 0, +VERB_SCOPE(, verb_calendar, "calendar", "SPEC...\0", 2, VERB_ANY, 0, "Validate repetitive calendar time events"); -VERB_SCOPE(, verb_timestamp, "timestamp", "TIMESTAMP...", 2, VERB_ANY, 0, +VERB_SCOPE(, verb_timestamp, "timestamp", "TIMESTAMP...\0", 2, VERB_ANY, 0, "Validate a timestamp"); -VERB_SCOPE(, verb_timespan, "timespan", "SPAN...", 2, VERB_ANY, 0, +VERB_SCOPE(, verb_timespan, "timespan", "SPAN...\0", 2, VERB_ANY, 0, "Validate a time span"); VERB_GROUP("Unit & Service Analysis"); -VERB_SCOPE(, verb_verify, "verify", "FILE...", 2, VERB_ANY, 0, +VERB_SCOPE(, verb_verify, "verify", "FILE...\0", 2, VERB_ANY, 0, "Check unit files for correctness"); -VERB_SCOPE(, verb_security, "security", "[UNIT...]", VERB_ANY, VERB_ANY, 0, +VERB_SCOPE(, verb_security, "security", "[UNIT...]\0", VERB_ANY, VERB_ANY, 0, "Analyze security of unit"); -VERB_SCOPE(, verb_fdstore, "fdstore", "SERVICE...", 2, VERB_ANY, 0, +VERB_SCOPE(, verb_fdstore, "fdstore", "SERVICE...\0", 2, VERB_ANY, 0, "Show file descriptor store contents of service"); -VERB_SCOPE(, verb_malloc, "malloc", "[D-BUS SERVICE...]", VERB_ANY, VERB_ANY, 0, +VERB_SCOPE(, verb_malloc, "malloc", "[D-BUS SERVICE...]\0", VERB_ANY, VERB_ANY, 0, "Dump malloc stats of a D-Bus service"); -VERB_SCOPE(, verb_unit_gdb, "unit-gdb", "SERVICE", 2, VERB_ANY, 0, +VERB_SCOPE(, verb_unit_gdb, "unit-gdb", "SERVICE\0", 2, VERB_ANY, 0, "Attach a debugger to the given running service"); -VERB_SCOPE(, verb_unit_shell, "unit-shell", "SERVICE [COMMAND ...]", 2, VERB_ANY, 0, +VERB_SCOPE(, verb_unit_shell, "unit-shell", "SERVICE [COMMAND ...]\0", 2, VERB_ANY, 0, "Run command on the namespace of the service"); VERB_GROUP("Executable Analysis"); -VERB_SCOPE(, verb_elf_inspection, "inspect-elf", "FILE...", 2, VERB_ANY, 0, +VERB_SCOPE(, verb_elf_inspection, "inspect-elf", "FILE...\0", 2, VERB_ANY, 0, "Parse and print ELF package metadata"); -VERB_SCOPE(, verb_dlopen_metadata, "dlopen-metadata", "FILE", 2, 2, 0, +VERB_SCOPE(, verb_dlopen_metadata, "dlopen-metadata", "FILE\0", 2, 2, 0, "Parse and print ELF dlopen metadata"); VERB_GROUP("TPM Operations"); @@ -291,11 +291,11 @@ VERB_SCOPE(, verb_has_tpm2, "has-tpm2", NULL, VERB_ANY, 1, 0, "Report whether TPM2 support is available"); VERB_SCOPE(, verb_identify_tpm2, "identify-tpm2", NULL, VERB_ANY, 1, 0, "Show TPM2 vendor information"); -VERB_SCOPE(, verb_pcrs, "pcrs", "[PCR...]", VERB_ANY, VERB_ANY, 0, +VERB_SCOPE(, verb_pcrs, "pcrs", "[PCR...]\0", VERB_ANY, VERB_ANY, 0, "Show TPM2 PCRs and their names"); -VERB_SCOPE(, verb_nvpcrs, "nvpcrs", "[NVPCR...]", VERB_ANY, VERB_ANY, 0, +VERB_SCOPE(, verb_nvpcrs, "nvpcrs", "[NVPCR...]\0", VERB_ANY, VERB_ANY, 0, "Show additional TPM2 PCRs stored in NV indexes"); -VERB_SCOPE(, verb_srk, "srk", "[>FILE]", VERB_ANY, 1, 0, +VERB_SCOPE(, verb_srk, "srk", "[>FILE]\0", VERB_ANY, 1, 0, "Write TPM2 SRK (to FILE)"); /* The following are deprecated and not shown in --help. */ diff --git a/src/backlight/backlight.c b/src/backlight/backlight.c index 6aad85a024..175a773dd7 100644 --- a/src/backlight/backlight.c +++ b/src/backlight/backlight.c @@ -530,7 +530,7 @@ static int device_new_from_arg(const char *s, sd_device **ret) { return 1; /* Found. */ } -VERB(verb_load, "load", "[backlight|leds]:DEVICE", 2, 2, VERB_ONLINE_ONLY, +VERB(verb_load, "load", "[backlight|leds]:DEVICE\0", 2, 2, VERB_ONLINE_ONLY, "Set brightness to be the previously saved value"); static int verb_load(int argc, char *argv[], uintptr_t _data, void *userdata) { _cleanup_(sd_device_unrefp) sd_device *device = NULL; @@ -582,7 +582,7 @@ static int verb_load(int argc, char *argv[], uintptr_t _data, void *userdata) { return 0; } -VERB(verb_save, "save", "[backlight|leds]:DEVICE", 2, 2, VERB_ONLINE_ONLY, +VERB(verb_save, "save", "[backlight|leds]:DEVICE\0", 2, 2, VERB_ONLINE_ONLY, "Save current brightness"); static int verb_save(int argc, char *argv[], uintptr_t _data, void *userdata) { _cleanup_(sd_device_unrefp) sd_device *device = NULL; diff --git a/src/bootctl/bootctl.c b/src/bootctl/bootctl.c index 5e41b46b10..a99ca237e9 100644 --- a/src/bootctl/bootctl.c +++ b/src/bootctl/bootctl.c @@ -302,7 +302,7 @@ VERB_GROUP("Generic EFI Firmware/Boot Loader Commands"); VERB_SCOPE(, verb_status, "status", NULL, VERB_ANY, 1, VERB_DEFAULT, "Show status of installed boot loader and EFI variables"); -VERB_SCOPE(, verb_reboot_to_firmware, "reboot-to-firmware", "[BOOL]", VERB_ANY, 2, 0, +VERB_SCOPE(, verb_reboot_to_firmware, "reboot-to-firmware", "[BOOL]\0", VERB_ANY, 2, 0, "Query or set reboot-to-firmware EFI flag"); VERB_GROUP("Boot Loader Specification Commands"); @@ -310,10 +310,10 @@ VERB_GROUP("Boot Loader Specification Commands"); VERB_SCOPE_NOARG(, verb_list, "list", "List boot loader entries"); -VERB_SCOPE(, verb_unlink, "unlink", "ID", VERB_ANY, 2, 0, +VERB_SCOPE(, verb_unlink, "unlink", "ID\0", VERB_ANY, 2, 0, "Remove boot loader entry"); -VERB_SCOPE(, verb_link, "link", "KERNEL", 2, 2, 0, +VERB_SCOPE(, verb_link, "link", "KERNEL\0", 2, 2, 0, "Create boot loader entry for specified kernel"); VERB_SCOPE_NOARG(, verb_link_auto, "link-auto", @@ -324,22 +324,22 @@ VERB_SCOPE_NOARG(, verb_cleanup, "cleanup", VERB_GROUP("Boot Loader Interface Commands"); -VERB_SCOPE(, verb_set_efivar, "set-default", "ID", 2, 2, 0, +VERB_SCOPE(, verb_set_efivar, "set-default", "ID\0", 2, 2, 0, "Set default boot loader entry"); -VERB_SCOPE(, verb_set_efivar, "set-oneshot", "ID", 2, 2, 0, +VERB_SCOPE(, verb_set_efivar, "set-oneshot", "ID\0", 2, 2, 0, "Set default boot loader entry, for next boot only"); -VERB_SCOPE(, verb_set_efivar, "set-sysfail", "ID", 2, 2, 0, +VERB_SCOPE(, verb_set_efivar, "set-sysfail", "ID\0", 2, 2, 0, "Set boot loader entry used in case of a system failure"); -VERB_SCOPE(, verb_set_efivar, "set-timeout", "SECONDS", 2, 2, 0, +VERB_SCOPE(, verb_set_efivar, "set-timeout", "SECONDS\0", 2, 2, 0, "Set the menu timeout"); -VERB_SCOPE(, verb_set_efivar, "set-timeout-oneshot", "SECONDS", 2, 2, 0, +VERB_SCOPE(, verb_set_efivar, "set-timeout-oneshot", "SECONDS\0", 2, 2, 0, "Set the menu timeout for the next boot only"); -VERB_SCOPE(, verb_set_efivar, "set-preferred", "ID", 2, 2, 0, +VERB_SCOPE(, verb_set_efivar, "set-preferred", "ID\0", 2, 2, 0, /* help= */ NULL); VERB_GROUP("systemd-boot Commands"); @@ -361,10 +361,10 @@ VERB_SCOPE_NOARG(, verb_random_seed, "random-seed", VERB_GROUP("Kernel Image Commands"); -VERB_SCOPE(, verb_kernel_identify, "kernel-identify", "KERNEL-IMAGE", 2, 2, 0, +VERB_SCOPE(, verb_kernel_identify, "kernel-identify", "KERNEL-IMAGE\0", 2, 2, 0, "Identify kernel image type"); -VERB_SCOPE(, verb_kernel_inspect, "kernel-inspect", "KERNEL-IMAGE", 2, 2, 0, +VERB_SCOPE(, verb_kernel_inspect, "kernel-inspect", "KERNEL-IMAGE\0", 2, 2, 0, "Prints details about the kernel image"); static int parse_argv(int argc, char *argv[], char ***ret_args) { diff --git a/src/busctl/busctl.c b/src/busctl/busctl.c index ab784e2cdb..88eba5a512 100644 --- a/src/busctl/busctl.c +++ b/src/busctl/busctl.c @@ -547,7 +547,7 @@ static int tree_one(sd_bus *bus, const char *service) { return r; } -VERB(verb_tree, "tree", "[SERVICE…]", VERB_ANY, VERB_ANY, 0, +VERB(verb_tree, "tree", "[SERVICE…]\0", VERB_ANY, VERB_ANY, 0, "Show object tree of service"); static int verb_tree(int argc, char *argv[], uintptr_t _data, void *userdata) { _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL; @@ -1008,7 +1008,7 @@ static int members_flags_to_string(const Member *m, char **ret) { return 0; } -VERB(verb_introspect, "introspect", "SERVICE OBJECT [INTERFACE]", 3, 4, 0, +VERB(verb_introspect, "introspect", "SERVICE OBJECT [INTERFACE]\0", 3, 4, 0, "Introspect an object"); static int verb_introspect(int argc, char *argv[], uintptr_t _data, void *userdata) { static const XMLIntrospectOps ops = { @@ -1391,13 +1391,13 @@ static int monitor(int argc, char **argv, int (*dump)(sd_bus_message *m, FILE *f } } -VERB(verb_monitor, "monitor", "[SERVICE…]", VERB_ANY, VERB_ANY, 0, +VERB(verb_monitor, "monitor", "[SERVICE…]\0", VERB_ANY, VERB_ANY, 0, "Show bus traffic"); static int verb_monitor(int argc, char *argv[], uintptr_t _data, void *userdata) { return monitor(argc, argv, sd_json_format_enabled(arg_json_format_flags) ? message_json : message_dump); } -VERB(verb_capture, "capture", "[SERVICE…]", VERB_ANY, VERB_ANY, 0, +VERB(verb_capture, "capture", "[SERVICE…]\0", VERB_ANY, VERB_ANY, 0, "Capture bus traffic as pcap"); static int verb_capture(int argc, char *argv[], uintptr_t _data, void *userdata) { _cleanup_free_ char *osname = NULL; @@ -1426,7 +1426,7 @@ static int verb_capture(int argc, char *argv[], uintptr_t _data, void *userdata) return r; } -VERB(verb_status, "status", "[SERVICE]", VERB_ANY, 2, 0, +VERB(verb_status, "status", "[SERVICE]\0", VERB_ANY, 2, 0, "Show bus service, process, or bus owner credentials"); static int verb_status(int argc, char *argv[], uintptr_t _data, void *userdata) { _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL; @@ -1798,7 +1798,7 @@ static int bus_message_dump(sd_bus_message *m, uint64_t flags) { return 0; } -VERB(verb_call, "call", "SERVICE OBJECT INTERFACE METHOD [SIGNATURE [ARGUMENT…]]", 5, VERB_ANY, 0, +VERB(verb_call, "call", "SERVICE OBJECT INTERFACE METHOD [SIGNATURE [ARGUMENT…]]\0", 5, VERB_ANY, 0, "Call a method"); static int verb_call(int argc, char *argv[], uintptr_t _data, void *userdata) { _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL; @@ -1867,7 +1867,7 @@ static int verb_call(int argc, char *argv[], uintptr_t _data, void *userdata) { return bus_message_dump(reply, /* flags= */ 0); } -VERB(verb_emit, "emit", "OBJECT INTERFACE SIGNAL [SIGNATURE [ARGUMENT…]]", 4, VERB_ANY, 0, +VERB(verb_emit, "emit", "OBJECT INTERFACE SIGNAL [SIGNATURE [ARGUMENT…]]\0", 4, VERB_ANY, 0, "Emit a signal"); static int verb_emit(int argc, char *argv[], uintptr_t _data, void *userdata) { _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL; @@ -1914,7 +1914,7 @@ static int verb_emit(int argc, char *argv[], uintptr_t _data, void *userdata) { return 0; } -VERB(verb_get_property, "get-property", "SERVICE OBJECT INTERFACE PROPERTY…", 5, VERB_ANY, 0, +VERB(verb_get_property, "get-property", "SERVICE OBJECT INTERFACE PROPERTY…\0", 5, VERB_ANY, 0, "Get property value"); static int verb_get_property(int argc, char *argv[], uintptr_t _data, void *userdata) { _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL; @@ -1974,7 +1974,7 @@ static int on_bus_signal(sd_bus_message *msg, void *userdata, sd_bus_error *ret_ return 0; } -VERB(verb_wait, "wait", "[SERVICE] OBJECT INTERFACE SIGNAL", 4, 5, 0, +VERB(verb_wait, "wait", "[SERVICE] OBJECT INTERFACE SIGNAL\0", 4, 5, 0, "Wait for a signal"); static int verb_wait(int argc, char *argv[], uintptr_t _data, void *userdata) { _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL; @@ -2024,7 +2024,7 @@ static int verb_wait(int argc, char *argv[], uintptr_t _data, void *userdata) { return sd_event_loop(e); } -VERB(verb_set_property, "set-property", "SERVICE OBJECT INTERFACE PROPERTY SIGNATURE ARGUMENT…", 6, VERB_ANY, 0, +VERB(verb_set_property, "set-property", "SERVICE OBJECT INTERFACE PROPERTY SIGNATURE ARGUMENT…\0", 6, VERB_ANY, 0, "Set property value"); static int verb_set_property(int argc, char *argv[], uintptr_t _data, void *userdata) { _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL; diff --git a/src/clonesetup/clonesetup.c b/src/clonesetup/clonesetup.c index 8827dfb98f..e84728534d 100644 --- a/src/clonesetup/clonesetup.c +++ b/src/clonesetup/clonesetup.c @@ -103,7 +103,7 @@ static int clone_device( return 0; } -VERB(verb_add, "add", "NAME SOURCE DEST METADATA [OPTIONS]", 5, 6, 0, "Create a dm-clone device"); +VERB(verb_add, "add", "NAME SOURCE DEST METADATA [OPTIONS]\0", 5, 6, 0, "Create a dm-clone device"); /* Arguments: systemd-clonesetup add NAME SOURCE DEST METADATA [OPTIONS] */ static int verb_add(int argc, char *argv[], uintptr_t data, void *userdata) { @@ -126,7 +126,7 @@ static int verb_add(int argc, char *argv[], uintptr_t data, void *userdata) { return clone_device(name, src, dst, meta, options); } -VERB(verb_remove, "remove", "NAME", 2, 2, 0, "Remove a dm-clone device"); +VERB(verb_remove, "remove", "NAME\0", 2, 2, 0, "Remove a dm-clone device"); static int verb_remove(int argc, char *argv[], uintptr_t data, void *userdata) { const char *name = ASSERT_PTR(argv[1]); diff --git a/src/coredump/coredumpctl.c b/src/coredump/coredumpctl.c index 1ceddf33de..14f2b33b9c 100644 --- a/src/coredump/coredumpctl.c +++ b/src/coredump/coredumpctl.c @@ -214,15 +214,15 @@ static int parse_argv(int argc, char *argv[], char ***remaining_args) { return 1; } -VERB_SCOPE(, verb_dump_list, "list", "[MATCHES…]", VERB_ANY, VERB_ANY, VERB_DEFAULT, +VERB_SCOPE(, verb_dump_list, "list", "[MATCHES…]\0", VERB_ANY, VERB_ANY, VERB_DEFAULT, "List available coredumps"); -VERB_SCOPE(, verb_dump_list, "info", "[MATCHES…]", VERB_ANY, VERB_ANY, 0, +VERB_SCOPE(, verb_dump_list, "info", "[MATCHES…]\0", VERB_ANY, VERB_ANY, 0, "Show detailed information about one or more coredumps"); -VERB_SCOPE(, verb_dump_core, "dump", "[MATCHES…]", VERB_ANY, VERB_ANY, 0, +VERB_SCOPE(, verb_dump_core, "dump", "[MATCHES…]\0", VERB_ANY, VERB_ANY, 0, "Print first matching coredump to stdout"); -VERB_SCOPE(, verb_run_debug, "debug", "[MATCHES…]", VERB_ANY, VERB_ANY, 0, +VERB_SCOPE(, verb_run_debug, "debug", "[MATCHES…]\0", VERB_ANY, VERB_ANY, 0, "Start a debugger for the first matching coredump"); -VERB_SCOPE(, verb_run_debug, "gdb", "[MATCHES…]", VERB_ANY, VERB_ANY, 0, +VERB_SCOPE(, verb_run_debug, "gdb", "[MATCHES…]\0", VERB_ANY, VERB_ANY, 0, /* help= */ NULL); static int check_units_active(void) { diff --git a/src/creds/creds.c b/src/creds/creds.c index 97143030cb..cc6906ae1b 100644 --- a/src/creds/creds.c +++ b/src/creds/creds.c @@ -473,7 +473,7 @@ static int write_blob(FILE *f, const void *data, size_t size) { return 0; } -VERB(verb_cat, "cat", "CREDENTIAL...", 2, VERB_ANY, 0, +VERB(verb_cat, "cat", "CREDENTIAL...\0", 2, VERB_ANY, 0, "Show contents of specified credentials"); static int verb_cat(int argc, char *argv[], uintptr_t _data, void *userdata) { usec_t timestamp; @@ -561,7 +561,7 @@ static int verb_cat(int argc, char *argv[], uintptr_t _data, void *userdata) { return ret; } -VERB(verb_encrypt, "encrypt", "INPUT OUTPUT", 3, 3, 0, +VERB(verb_encrypt, "encrypt", "INPUT OUTPUT\0", 3, 3, 0, "Encrypt plaintext credential file and write to ciphertext credential file"); static int verb_encrypt(int argc, char *argv[], uintptr_t _data, void *userdata) { _cleanup_(iovec_done_erase) struct iovec plaintext = {}, output = {}; @@ -671,7 +671,7 @@ static int verb_encrypt(int argc, char *argv[], uintptr_t _data, void *userdata) return EXIT_SUCCESS; } -VERB(verb_decrypt, "decrypt", "INPUT [OUTPUT]", 2, 3, 0, +VERB(verb_decrypt, "decrypt", "INPUT [OUTPUT]\0", 2, 3, 0, "Decrypt ciphertext credential file and write to plaintext credential file"); static int verb_decrypt(int argc, char *argv[], uintptr_t _data, void *userdata) { _cleanup_(iovec_done_erase) struct iovec input = {}, plaintext = {}; diff --git a/src/cryptsetup/cryptsetup.c b/src/cryptsetup/cryptsetup.c index 7f1ad550b3..fb616a3426 100644 --- a/src/cryptsetup/cryptsetup.c +++ b/src/cryptsetup/cryptsetup.c @@ -2484,7 +2484,7 @@ static int discover_key(const char *key_file, const char *volume, TokenType toke return r; } -VERB(verb_attach, "attach", "VOLUME SOURCE-DEVICE [KEY-FILE] [CONFIG]", 3, 5, 0, +VERB(verb_attach, "attach", "VOLUME SOURCE-DEVICE [KEY-FILE] [CONFIG]\0", 3, 5, 0, "Attach an encrypted block device"); static int verb_attach(int argc, char *argv[], uintptr_t _data, void *userdata) { _cleanup_(crypt_freep) struct crypt_device *cd = NULL; @@ -2731,7 +2731,7 @@ static int verb_attach(int argc, char *argv[], uintptr_t _data, void *userdata) return 0; } -VERB(verb_detach, "detach", "VOLUME", 2, 2, 0, +VERB(verb_detach, "detach", "VOLUME\0", 2, 2, 0, "Detach an encrypted block device"); static int verb_detach(int argc, char *argv[], uintptr_t _data, void *userdata) { _cleanup_(crypt_freep) struct crypt_device *cd = NULL; diff --git a/src/home/homectl.c b/src/home/homectl.c index 5fd6b18f62..0fcf210f93 100644 --- a/src/home/homectl.c +++ b/src/home/homectl.c @@ -711,7 +711,7 @@ static int inspect_home(sd_bus *bus, const char *name) { return 0; } -VERB(verb_inspect_homes, "inspect", "USER…", VERB_ANY, VERB_ANY, 0, +VERB(verb_inspect_homes, "inspect", "USER…\0", VERB_ANY, VERB_ANY, 0, "Inspect a home area"); static int verb_inspect_homes(int argc, char *argv[], uintptr_t _data, void *userdata) { _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL; @@ -1459,7 +1459,7 @@ static int create_home_common(sd_json_variant *input, bool show_enforce_password return 0; } -VERB(verb_create_home, "create", "USER", VERB_ANY, 2, 0, +VERB(verb_create_home, "create", "USER\0", VERB_ANY, 2, 0, "Create a home area"); static int verb_create_home(int argc, char *argv[], uintptr_t _data, void *userdata) { int r; @@ -1641,7 +1641,7 @@ static int home_record_reset_human_interaction_permission(UserRecord *hr) { return 0; } -VERB(verb_update_home, "update", "USER", VERB_ANY, 2, 0, +VERB(verb_update_home, "update", "USER\0", VERB_ANY, 2, 0, "Update a home area"); static int verb_update_home(int argc, char *argv[], uintptr_t _data, void *userdata) { _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL; @@ -1824,7 +1824,7 @@ static int verb_update_home(int argc, char *argv[], uintptr_t _data, void *userd return 0; } -VERB(verb_passwd_home, "passwd", "USER", VERB_ANY, 2, 0, +VERB(verb_passwd_home, "passwd", "USER\0", VERB_ANY, 2, 0, "Change password of a home area"); static int verb_passwd_home(int argc, char *argv[], uintptr_t _data, void *userdata) { _cleanup_(user_record_unrefp) UserRecord *old_secret = NULL, *new_secret = NULL; @@ -1943,7 +1943,7 @@ static int parse_disk_size(const char *t, uint64_t *ret) { return 0; } -VERB(verb_resize_home, "resize", "USER SIZE", 2, 3, 0, +VERB(verb_resize_home, "resize", "USER SIZE\0", 2, 3, 0, "Resize a home area"); static int verb_resize_home(int argc, char *argv[], uintptr_t _data, void *userdata) { _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL; @@ -2007,7 +2007,7 @@ static int verb_resize_home(int argc, char *argv[], uintptr_t _data, void *userd return 0; } -VERB(verb_remove_home, "remove", "USER…", 2, VERB_ANY, 0, +VERB(verb_remove_home, "remove", "USER…\0", 2, VERB_ANY, 0, "Remove a home area"); static int verb_remove_home(int argc, char *argv[], uintptr_t _data, void *userdata) { _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL; @@ -2043,7 +2043,7 @@ static int verb_remove_home(int argc, char *argv[], uintptr_t _data, void *userd } VERB_GROUP("Advanced User Manipulation Commands"); -VERB(verb_activate_home, "activate", "USER…", 2, VERB_ANY, 0, +VERB(verb_activate_home, "activate", "USER…\0", 2, VERB_ANY, 0, "Activate a home area"); static int verb_activate_home(int argc, char *argv[], uintptr_t _data, void *userdata) { _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL; @@ -2093,7 +2093,7 @@ static int verb_activate_home(int argc, char *argv[], uintptr_t _data, void *use return ret; } -VERB(verb_deactivate_home, "deactivate", "USER…", 2, VERB_ANY, 0, +VERB(verb_deactivate_home, "deactivate", "USER…\0", 2, VERB_ANY, 0, "Deactivate a home area"); static int verb_deactivate_home(int argc, char *argv[], uintptr_t _data, void *userdata) { _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL; @@ -2168,7 +2168,7 @@ static int verb_deactivate_all_homes(int argc, char *argv[], uintptr_t _data, vo return 0; } -VERB(verb_with_home, "with", "USER [COMMAND…]", 2, VERB_ANY, 0, +VERB(verb_with_home, "with", "USER [COMMAND…]\0", 2, VERB_ANY, 0, "Run shell or command with access to a home area"); static int verb_with_home(int argc, char *argv[], uintptr_t _data, void *userdata) { _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL; @@ -2327,7 +2327,7 @@ static int authenticate_home(sd_bus *bus, const char *name) { } } -VERB(verb_authenticate_homes, "authenticate", "USER…", VERB_ANY, VERB_ANY, 0, +VERB(verb_authenticate_homes, "authenticate", "USER…\0", VERB_ANY, VERB_ANY, 0, "Authenticate a home area"); static int verb_authenticate_homes(int argc, char *argv[], uintptr_t _data, void *userdata) { _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL; @@ -2355,7 +2355,7 @@ static int verb_authenticate_homes(int argc, char *argv[], uintptr_t _data, void } VERB_GROUP("User Migration Commands"); -VERB(verb_adopt_home, "adopt", "PATH…", VERB_ANY, VERB_ANY, 0, +VERB(verb_adopt_home, "adopt", "PATH…\0", VERB_ANY, VERB_ANY, 0, "Add an existing home area on this system"); static int verb_adopt_home(int argc, char *argv[], uintptr_t _data, void *userdata) { int r, ret = 0; @@ -2439,7 +2439,7 @@ static int register_home_one(sd_bus *bus, FILE *f, const char *path) { return register_home_common(bus, v); } -VERB(verb_register_home, "register", "PATH…", VERB_ANY, VERB_ANY, 0, +VERB(verb_register_home, "register", "PATH…\0", VERB_ANY, VERB_ANY, 0, "Register a user record locally"); static int verb_register_home(int argc, char *argv[], uintptr_t _data, void *userdata) { int r; @@ -2472,7 +2472,7 @@ static int verb_register_home(int argc, char *argv[], uintptr_t _data, void *use return r; } -VERB(verb_unregister_home, "unregister", "USER…", 2, VERB_ANY, 0, +VERB(verb_unregister_home, "unregister", "USER…\0", 2, VERB_ANY, 0, "Unregister a user record locally"); static int verb_unregister_home(int argc, char *argv[], uintptr_t _data, void *userdata) { int r; @@ -2597,7 +2597,7 @@ static int verb_list_signing_keys(int argc, char *argv[], uintptr_t _data, void return 0; } -VERB(verb_get_signing_key, "get-signing-key", "[NAME…]", VERB_ANY, VERB_ANY, 0, +VERB(verb_get_signing_key, "get-signing-key", "[NAME…]\0", VERB_ANY, VERB_ANY, 0, "Get a named home signing key"); static int verb_get_signing_key(int argc, char *argv[], uintptr_t _data, void *userdata) { int r; @@ -2655,7 +2655,7 @@ static int add_signing_key_one(sd_bus *bus, const char *fn, FILE *key) { return 0; } -VERB(verb_add_signing_key, "add-signing-key", "FILE…", VERB_ANY, VERB_ANY, 0, +VERB(verb_add_signing_key, "add-signing-key", "FILE…\0", VERB_ANY, VERB_ANY, 0, "Add home signing key"); static int verb_add_signing_key(int argc, char *argv[], uintptr_t _data, void *userdata) { int r; @@ -2764,7 +2764,7 @@ static int remove_signing_key_one(sd_bus *bus, const char *fn) { return 0; } -VERB(verb_remove_signing_key, "remove-signing-key", "NAME…", 2, VERB_ANY, 0, +VERB(verb_remove_signing_key, "remove-signing-key", "NAME…\0", 2, VERB_ANY, 0, "Remove home signing key"); static int verb_remove_signing_key(int argc, char *argv[], uintptr_t _data, void *userdata) { int r; @@ -2782,7 +2782,7 @@ static int verb_remove_signing_key(int argc, char *argv[], uintptr_t _data, void } VERB_GROUP("Lock/Unlock Commands"); -VERB(verb_lock_home, "lock", "USER…", 2, VERB_ANY, 0, +VERB(verb_lock_home, "lock", "USER…\0", 2, VERB_ANY, 0, "Temporarily lock an active home area"); static int verb_lock_home(int argc, char *argv[], uintptr_t _data, void *userdata) { _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL; @@ -2815,7 +2815,7 @@ static int verb_lock_home(int argc, char *argv[], uintptr_t _data, void *userdat return ret; } -VERB(verb_unlock_home, "unlock", "USER…", 2, VERB_ANY, 0, +VERB(verb_unlock_home, "unlock", "USER…\0", 2, VERB_ANY, 0, "Unlock a temporarily locked home area"); static int verb_unlock_home(int argc, char *argv[], uintptr_t _data, void *userdata) { _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL; diff --git a/src/home/test-homectl-prompts.c b/src/home/test-homectl-prompts.c index 59bff32c44..5429eaaa5e 100644 --- a/src/home/test-homectl-prompts.c +++ b/src/home/test-homectl-prompts.c @@ -37,7 +37,7 @@ static int help(void) { return table_print_or_warn(options); } -VERB(verb_groups, "groups", "[USER]", VERB_ANY, 2, 0, "Select groups"); +VERB(verb_groups, "groups", "[USER]\0", VERB_ANY, 2, 0, "Select groups"); static int verb_groups(int argc, char *argv[], uintptr_t _data, void *userdata) { assert(argv); @@ -55,7 +55,7 @@ static int verb_groups(int argc, char *argv[], uintptr_t _data, void *userdata) return 0; } -VERB(verb_shell, "shell", "[USER]", VERB_ANY, 2, 0, "Select shell"); +VERB(verb_shell, "shell", "[USER]\0", VERB_ANY, 2, 0, "Select shell"); static int verb_shell(int argc, char *argv[], uintptr_t _data, void *userdata) { assert(argv); diff --git a/src/hostname/hostnamectl.c b/src/hostname/hostnamectl.c index 6bc4d36217..274a2302da 100644 --- a/src/hostname/hostnamectl.c +++ b/src/hostname/hostnamectl.c @@ -710,42 +710,42 @@ static int verb_set_hostname(int argc, char *argv[], uintptr_t _data, void *user return ret; } -VERB(verb_get_or_set_hostname, "hostname", "[NAME]", VERB_ANY, 2, 0, "Get/set system hostname"); -VERB(verb_get_or_set_hostname, "set-hostname", "NAME", 2, 2, 0, NULL); /* obsolete */ +VERB(verb_get_or_set_hostname, "hostname", "[NAME]\0", VERB_ANY, 2, 0, "Get/set system hostname"); +VERB(verb_get_or_set_hostname, "set-hostname", "NAME\0", 2, 2, 0, NULL); /* obsolete */ static int verb_get_or_set_hostname(int argc, char *argv[], uintptr_t data, void *userdata) { return argc == 1 ? get_hostname_based_on_flag(userdata) : verb_set_hostname(argc, argv, data, userdata); } -VERB(verb_get_or_set_icon_name, "icon-name", "[NAME]", VERB_ANY, 2, 0, "Get/set icon name for host"); -VERB(verb_get_or_set_icon_name, "set-icon-name", "NAME", 2, 2, 0, NULL); /* obsolete */ +VERB(verb_get_or_set_icon_name, "icon-name", "[NAME]\0", VERB_ANY, 2, 0, "Get/set icon name for host"); +VERB(verb_get_or_set_icon_name, "set-icon-name", "NAME\0", 2, 2, 0, NULL); /* obsolete */ static int verb_get_or_set_icon_name(int argc, char *argv[], uintptr_t _data, void *userdata) { return argc == 1 ? get_one_name(userdata, "IconName", NULL) : set_simple_string(userdata, "icon", "SetIconName", argv[1]); } -VERB(verb_get_or_set_chassis, "chassis", "[NAME]", VERB_ANY, 2, 0, "Get/set chassis type for host"); -VERB(verb_get_or_set_chassis, "set-chassis", "NAME", 2, 2, 0, NULL); /* obsolete */ +VERB(verb_get_or_set_chassis, "chassis", "[NAME]\0", VERB_ANY, 2, 0, "Get/set chassis type for host"); +VERB(verb_get_or_set_chassis, "set-chassis", "NAME\0", 2, 2, 0, NULL); /* obsolete */ static int verb_get_or_set_chassis(int argc, char *argv[], uintptr_t _data, void *userdata) { return argc == 1 ? get_one_name(userdata, "Chassis", NULL) : set_simple_string(userdata, "chassis", "SetChassis", argv[1]); } -VERB(verb_get_or_set_deployment, "deployment", "[NAME]", VERB_ANY, 2, 0, "Get/set deployment environment for host"); -VERB(verb_get_or_set_deployment, "set-deployment", "NAME", 2, 2, 0, NULL); /* obsolete */ +VERB(verb_get_or_set_deployment, "deployment", "[NAME]\0", VERB_ANY, 2, 0, "Get/set deployment environment for host"); +VERB(verb_get_or_set_deployment, "set-deployment", "NAME\0", 2, 2, 0, NULL); /* obsolete */ static int verb_get_or_set_deployment(int argc, char *argv[], uintptr_t _data, void *userdata) { return argc == 1 ? get_one_name(userdata, "Deployment", NULL) : set_simple_string(userdata, "deployment", "SetDeployment", argv[1]); } -VERB(verb_get_or_set_location, "location", "[NAME]", VERB_ANY, 2, 0, "Get/set location for host"); -VERB(verb_get_or_set_location, "set-location", "NAME", 2, 2, 0, NULL); /* obsolete */ +VERB(verb_get_or_set_location, "location", "[NAME]\0", VERB_ANY, 2, 0, "Get/set location for host"); +VERB(verb_get_or_set_location, "set-location", "NAME\0", 2, 2, 0, NULL); /* obsolete */ static int verb_get_or_set_location(int argc, char *argv[], uintptr_t _data, void *userdata) { return argc == 1 ? get_one_name(userdata, "Location", NULL) : set_simple_string(userdata, "location", "SetLocation", argv[1]); } -VERB(verb_get_or_set_tags, "tags", "[TAG …]", VERB_ANY, VERB_ANY, 0, "Get/set machine tags for host"); +VERB(verb_get_or_set_tags, "tags", "[TAG …]\0", VERB_ANY, VERB_ANY, 0, "Get/set machine tags for host"); static int verb_get_or_set_tags(int argc, char *argv[], uintptr_t _data, void *userdata) { _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL; sd_bus *bus = ASSERT_PTR(userdata); diff --git a/src/hwdb/hwdb.c b/src/hwdb/hwdb.c index d0b94493bc..68dbc5116e 100644 --- a/src/hwdb/hwdb.c +++ b/src/hwdb/hwdb.c @@ -20,7 +20,7 @@ COMMAND( .man_pages = "systemd-hwdb(8)\0", ); -VERB(verb_query, "query", "MODALIAS", 2, 2, 0, +VERB(verb_query, "query", "MODALIAS\0", 2, 2, 0, "Query database and print result"); static int verb_query(int argc, char *argv[], uintptr_t _data, void *userdata) { return hwdb_query(argv[1], arg_root); diff --git a/src/id128/id128.c b/src/id128/id128.c index defb4f756f..45f66c2782 100644 --- a/src/id128/id128.c +++ b/src/id128/id128.c @@ -175,7 +175,7 @@ static int show_one(Table **table, const char *name, sd_id128_t uuid, bool first arg_mode == ID128_PRINT_ID128 ? TABLE_ID128 : TABLE_UUID, uuid); } -VERB(verb_show, "show", "[NAME|UUID]", VERB_ANY, VERB_ANY, 0, "Print one or more UUIDs"); +VERB(verb_show, "show", "[NAME|UUID]\0", VERB_ANY, VERB_ANY, 0, "Print one or more UUIDs"); static int verb_show(int argc, char *argv[], uintptr_t _data, void *userdata) { _cleanup_(table_unrefp) Table *table = NULL; int r; diff --git a/src/import/export.c b/src/import/export.c index 41d3d247e1..91949ae54c 100644 --- a/src/import/export.c +++ b/src/import/export.c @@ -49,7 +49,7 @@ static void on_tar_finished(TarExport *export, int error, void *userdata) { sd_event_exit(event, ABS(error)); } -VERB(verb_export_tar, "tar", "NAME [FILE]", 2, 3, 0, +VERB(verb_export_tar, "tar", "NAME [FILE]\0", 2, 3, 0, "Export a TAR image"); static int verb_export_tar(int argc, char *argv[], uintptr_t _data, void *userdata) { _cleanup_(tar_export_unrefp) TarExport *export = NULL; @@ -132,7 +132,7 @@ static void on_raw_finished(RawExport *export, int error, void *userdata) { sd_event_exit(event, ABS(error)); } -VERB(verb_export_raw, "raw", "NAME [FILE]", 2, 3, 0, +VERB(verb_export_raw, "raw", "NAME [FILE]\0", 2, 3, 0, "Export a RAW image"); static int verb_export_raw(int argc, char *argv[], uintptr_t _data, void *userdata) { _cleanup_(raw_export_unrefp) RawExport *export = NULL; diff --git a/src/import/import-fs.c b/src/import/import-fs.c index b1b9b637aa..d5cad89956 100644 --- a/src/import/import-fs.c +++ b/src/import/import-fs.c @@ -113,7 +113,7 @@ static int progress_bytes(uint64_t nbytes, uint64_t bps, void *userdata) { return 0; } -VERB(verb_import_fs, "run", "DIRECTORY [NAME]", 2, 3, 0, +VERB(verb_import_fs, "run", "DIRECTORY [NAME]\0", 2, 3, 0, "Import a directory"); static int verb_import_fs(int argc, char *argv[], uintptr_t _data, void *userdata) { _cleanup_(rm_rf_subvolume_and_freep) char *temp_path = NULL; diff --git a/src/import/import.c b/src/import/import.c index 37eda8711f..190ad6079f 100644 --- a/src/import/import.c +++ b/src/import/import.c @@ -143,7 +143,7 @@ static void on_tar_finished(TarImport *import, int error, void *userdata) { sd_event_exit(event, ABS(error)); } -VERB(verb_tar, "tar", "FILE [NAME]", 2, 3, 0, "Import a TAR image"); +VERB(verb_tar, "tar", "FILE [NAME]\0", 2, 3, 0, "Import a TAR image"); static int verb_tar(int argc, char *argv[], uintptr_t _data, void *userdata) { _cleanup_(tar_import_unrefp) TarImport *import = NULL; _cleanup_free_ char *ll = NULL, *normalized = NULL; @@ -213,7 +213,7 @@ static void on_raw_finished(RawImport *import, int error, void *userdata) { sd_event_exit(event, ABS(error)); } -VERB(verb_raw, "raw", "FILE [NAME]", 2, 3, 0, "Import a RAW image"); +VERB(verb_raw, "raw", "FILE [NAME]\0", 2, 3, 0, "Import a RAW image"); static int verb_raw(int argc, char *argv[], uintptr_t _data, void *userdata) { _cleanup_(raw_import_unrefp) RawImport *import = NULL; _cleanup_free_ char *ll = NULL, *normalized = NULL; diff --git a/src/import/importctl.c b/src/import/importctl.c index 911799d188..59bfb89c11 100644 --- a/src/import/importctl.c +++ b/src/import/importctl.c @@ -268,7 +268,7 @@ static int transfer_image_common(sd_bus *bus, sd_bus_message *m) { return -r; } -VERB(verb_pull_tar, "pull-tar", "URL [NAME]", 2, 3, 0, "Download a TAR container image"); +VERB(verb_pull_tar, "pull-tar", "URL [NAME]\0", 2, 3, 0, "Download a TAR container image"); static int verb_pull_tar(int argc, char *argv[], uintptr_t _data, void *userdata) { _cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL; _cleanup_free_ char *l = NULL, *ll = NULL; @@ -342,7 +342,7 @@ static int verb_pull_tar(int argc, char *argv[], uintptr_t _data, void *userdata return transfer_image_common(bus, m); } -VERB(verb_pull_raw, "pull-raw", "URL [NAME]", 2, 3, 0, "Download a RAW container or VM image"); +VERB(verb_pull_raw, "pull-raw", "URL [NAME]\0", 2, 3, 0, "Download a RAW container or VM image"); static int verb_pull_raw(int argc, char *argv[], uintptr_t _data, void *userdata) { _cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL; _cleanup_free_ char *l = NULL, *ll = NULL; @@ -416,7 +416,7 @@ static int verb_pull_raw(int argc, char *argv[], uintptr_t _data, void *userdata return transfer_image_common(bus, m); } -VERB(verb_pull_oci, "pull-oci", "REF [NAME]", 2, 3, 0, "Download an OCI container image"); +VERB(verb_pull_oci, "pull-oci", "REF [NAME]\0", 2, 3, 0, "Download an OCI container image"); static int verb_pull_oci(int argc, char *argv[], uintptr_t _data, void *userdata) { _cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL; _cleanup_free_ char *l = NULL; @@ -472,7 +472,7 @@ static int verb_pull_oci(int argc, char *argv[], uintptr_t _data, void *userdata return transfer_image_common(bus, m); } -VERB(verb_import_tar, "import-tar", "FILE [NAME]", 2, 3, 0, "Import a local TAR container image"); +VERB(verb_import_tar, "import-tar", "FILE [NAME]\0", 2, 3, 0, "Import a local TAR container image"); static int verb_import_tar(int argc, char *argv[], uintptr_t _data, void *userdata) { _cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL; _cleanup_free_ char *ll = NULL, *fn = NULL; @@ -552,7 +552,7 @@ static int verb_import_tar(int argc, char *argv[], uintptr_t _data, void *userda return transfer_image_common(bus, m); } -VERB(verb_import_raw, "import-raw", "FILE [NAME]", 2, 3, 0, "Import a local RAW container or VM image"); +VERB(verb_import_raw, "import-raw", "FILE [NAME]\0", 2, 3, 0, "Import a local RAW container or VM image"); static int verb_import_raw(int argc, char *argv[], uintptr_t _data, void *userdata) { _cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL; _cleanup_free_ char *ll = NULL, *fn = NULL; @@ -632,7 +632,7 @@ static int verb_import_raw(int argc, char *argv[], uintptr_t _data, void *userda return transfer_image_common(bus, m); } -VERB(verb_import_fs, "import-fs", "DIRECTORY [NAME]", 2, 3, 0, "Import a local directory container image"); +VERB(verb_import_fs, "import-fs", "DIRECTORY [NAME]\0", 2, 3, 0, "Import a local directory container image"); static int verb_import_fs(int argc, char *argv[], uintptr_t _data, void *userdata) { _cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL; const char *local = NULL, *path = NULL; @@ -720,7 +720,7 @@ static void determine_compression_from_filename(const char *p) { arg_format = "zstd"; } -VERB(verb_export_tar, "export-tar", "NAME [FILE]", 2, 3, 0, "Export a TAR container image locally"); +VERB(verb_export_tar, "export-tar", "NAME [FILE]\0", 2, 3, 0, "Export a TAR container image locally"); static int verb_export_tar(int argc, char *argv[], uintptr_t _data, void *userdata) { _cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL; _cleanup_close_ int fd = -EBADF; @@ -780,7 +780,7 @@ static int verb_export_tar(int argc, char *argv[], uintptr_t _data, void *userda return transfer_image_common(bus, m); } -VERB(verb_export_raw, "export-raw", "NAME [FILE]", 2, 3, 0, "Export a RAW container or VM image locally"); +VERB(verb_export_raw, "export-raw", "NAME [FILE]\0", 2, 3, 0, "Export a RAW container or VM image locally"); static int verb_export_raw(int argc, char *argv[], uintptr_t _data, void *userdata) { _cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL; _cleanup_close_ int fd = -EBADF; @@ -945,7 +945,7 @@ static int verb_list_transfers(int argc, char *argv[], uintptr_t _data, void *us return 0; } -VERB(verb_cancel_transfer, "cancel-transfer", "[ID...]", 2, VERB_ANY, 0, "Cancel a transfer"); +VERB(verb_cancel_transfer, "cancel-transfer", "[ID...]\0", 2, VERB_ANY, 0, "Cancel a transfer"); static int verb_cancel_transfer(int argc, char *argv[], uintptr_t _data, void *userdata) { _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL; sd_bus *bus = ASSERT_PTR(userdata); diff --git a/src/import/pull.c b/src/import/pull.c index 3960a6e683..7f5a90196f 100644 --- a/src/import/pull.c +++ b/src/import/pull.c @@ -123,7 +123,7 @@ static void on_tar_finished(TarPull *pull, int error, void *userdata) { sd_event_exit(event, ABS(error)); } -VERB(verb_tar, "tar", "URL [NAME]", 2, 3, 0, "Download a TAR image"); +VERB(verb_tar, "tar", "URL [NAME]\0", 2, 3, 0, "Download a TAR image"); static int verb_tar(int argc, char *argv[], uintptr_t _data, void *userdata) { _cleanup_free_ char *ll = NULL, *normalized = NULL; _cleanup_(sd_event_unrefp) sd_event *event = NULL; @@ -194,7 +194,7 @@ static void on_raw_finished(RawPull *pull, int error, void *userdata) { sd_event_exit(event, ABS(error)); } -VERB(verb_raw, "raw", "URL [NAME]", 2, 3, 0, "Download a RAW image"); +VERB(verb_raw, "raw", "URL [NAME]\0", 2, 3, 0, "Download a RAW image"); static int verb_raw(int argc, char *argv[], uintptr_t _data, void *userdata) { _cleanup_free_ char *ll = NULL, *normalized = NULL; _cleanup_(sd_event_unrefp) sd_event *event = NULL; @@ -264,7 +264,7 @@ static void on_oci_finished(OciPull *pull, int error, void *userdata) { sd_event_exit(event, ABS(error)); } -VERB(verb_oci, "oci", "REF [NAME]", 2, 3, 0, "Download an OCI image"); +VERB(verb_oci, "oci", "REF [NAME]\0", 2, 3, 0, "Download an OCI image"); static int verb_oci(int argc, char *argv[], uintptr_t _data, void *userdata) { int r; diff --git a/src/integritysetup/integritysetup.c b/src/integritysetup/integritysetup.c index 742c1c7e05..0160b62ef4 100644 --- a/src/integritysetup/integritysetup.c +++ b/src/integritysetup/integritysetup.c @@ -82,7 +82,7 @@ static const char *integrity_algorithm_select(const void *key_file_buf) { return dm_integrity_algorithm_to_string(a); } -VERB(verb_attach, "attach", "VOLUME DEVICE [HMAC_KEY_FILE|-] [OPTIONS]", 3, 5, 0, +VERB(verb_attach, "attach", "VOLUME DEVICE [HMAC_KEY_FILE|-] [OPTIONS]\0", 3, 5, 0, "Attach an integrity protected block device"); static int verb_attach(int argc, char *argv[], uintptr_t _data, void *userdata) { _cleanup_(crypt_freep) struct crypt_device *cd = NULL; @@ -151,7 +151,7 @@ static int verb_attach(int argc, char *argv[], uintptr_t _data, void *userdata) return 0; } -VERB(verb_detach, "detach", "VOLUME", 2, 2, 0, +VERB(verb_detach, "detach", "VOLUME\0", 2, 2, 0, "Detach an integrity protected block device"); static int verb_detach(int argc, char *argv[], uintptr_t _data, void *userdata) { _cleanup_(crypt_freep) struct crypt_device *cd = NULL; diff --git a/src/kernel-install/kernel-install.c b/src/kernel-install/kernel-install.c index 4bae8cb1e5..0fb3a4034e 100644 --- a/src/kernel-install/kernel-install.c +++ b/src/kernel-install/kernel-install.c @@ -1281,7 +1281,7 @@ static int do_add( return context_execute(c); } -VERB(verb_add, "add", "[[[KERNEL-VERSION] KERNEL-IMAGE] [INITRD ...]]", 1, VERB_ANY, 0, +VERB(verb_add, "add", "[[[KERNEL-VERSION] KERNEL-IMAGE] [INITRD ...]]\0", 1, VERB_ANY, 0, "Add a kernel and initrd images to the boot partition"); static int verb_add(int argc, char *argv[], uintptr_t _data, void *userdata) { const char *version, *kernel; @@ -1394,7 +1394,7 @@ static int run_as_installkernel(char **args) { return verb_add(3, STRV_MAKE("add", args[0], args[1]), /* data= */ 0, /* userdata= */ NULL); } -VERB(verb_remove, "remove", "KERNEL-VERSION", 2, VERB_ANY, 0, +VERB(verb_remove, "remove", "KERNEL-VERSION\0", 2, VERB_ANY, 0, "Remove a kernel from the boot partition"); static int verb_remove(int argc, char *argv[], uintptr_t _data, void *userdata) { int r; @@ -1432,7 +1432,7 @@ static int verb_remove(int argc, char *argv[], uintptr_t _data, void *userdata) return context_execute(&c); } -VERB(verb_inspect, "inspect", "[[[KERNEL-VERSION] KERNEL-IMAGE] [INITRD ...]]", 1, VERB_ANY, VERB_DEFAULT, +VERB(verb_inspect, "inspect", "[[[KERNEL-VERSION] KERNEL-IMAGE] [INITRD ...]]\0", 1, VERB_ANY, VERB_DEFAULT, "Print details about the installation"); static int verb_inspect(int argc, char *argv[], uintptr_t _data, void *userdata) { _cleanup_(table_unrefp) Table *t = NULL; diff --git a/src/locale/localectl.c b/src/locale/localectl.c index 09c706553e..b8e434ec0d 100644 --- a/src/locale/localectl.c +++ b/src/locale/localectl.c @@ -186,7 +186,7 @@ static int verb_show_status(int argc, char *argv[], uintptr_t _data, void *userd return print_status_info(&info); } -VERB(verb_set_locale, "set-locale", "LOCALE...", 2, VERB_ANY, 0, +VERB(verb_set_locale, "set-locale", "LOCALE...\0", 2, VERB_ANY, 0, "Set system locale"); static int verb_set_locale(int argc, char *argv[], uintptr_t _data, void *userdata) { _cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL; @@ -232,7 +232,7 @@ static int verb_list_locales(int argc, char *argv[], uintptr_t _data, void *user return 0; } -VERB(verb_set_vconsole_keymap, "set-keymap", "MAP [MAP]", 2, 3, 0, +VERB(verb_set_vconsole_keymap, "set-keymap", "MAP [MAP]\0", 2, 3, 0, "Set console and X11 keyboard mappings"); static int verb_set_vconsole_keymap(int argc, char *argv[], uintptr_t _data, void *userdata) { _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL; @@ -275,7 +275,7 @@ static int verb_list_vconsole_keymaps(int argc, char *argv[], uintptr_t _data, v return 0; } -VERB(verb_set_x11_keymap, "set-x11-keymap", "LAYOUT [MODEL [VARIANT [OPTIONS]]]", 2, 5, 0, +VERB(verb_set_x11_keymap, "set-x11-keymap", "LAYOUT [MODEL [VARIANT [OPTIONS]]]\0", 2, 5, 0, "Set X11 and console keyboard mappings"); static int verb_set_x11_keymap(int argc, char *argv[], uintptr_t _data, void *userdata) { _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL; @@ -316,7 +316,7 @@ VERB_NOARG(verb_list_x11_keymaps, "list-x11-keymap-models", "Show known X11 keyboard mapping models"); VERB_NOARG(verb_list_x11_keymaps, "list-x11-keymap-layouts", "Show known X11 keyboard mapping layouts"); -VERB(verb_list_x11_keymaps, "list-x11-keymap-variants", "[LAYOUT]", VERB_ANY, 2, 0, +VERB(verb_list_x11_keymaps, "list-x11-keymap-variants", "[LAYOUT]\0", VERB_ANY, 2, 0, "Show known X11 keyboard mapping variants"); VERB_NOARG(verb_list_x11_keymaps, "list-x11-keymap-options", "Show known X11 keyboard mapping options"); diff --git a/src/login/loginctl.c b/src/login/loginctl.c index 67d7429749..8814b20234 100644 --- a/src/login/loginctl.c +++ b/src/login/loginctl.c @@ -930,9 +930,9 @@ static int get_bus_path_by_id( return strdup_to(ret, path); } -VERB(verb_show_session, "session-status", "[ID…]", VERB_ANY, VERB_ANY, 0, +VERB(verb_show_session, "session-status", "[ID…]\0", VERB_ANY, VERB_ANY, 0, "Show session status"); -VERB(verb_show_session, "show-session", "[ID…]", VERB_ANY, VERB_ANY, 0, +VERB(verb_show_session, "show-session", "[ID…]\0", VERB_ANY, VERB_ANY, 0, "Show properties of sessions or the manager"); static int verb_show_session(int argc, char *argv[], uintptr_t _data, void *userdata) { sd_bus *bus = ASSERT_PTR(userdata); @@ -980,11 +980,11 @@ static int verb_show_session(int argc, char *argv[], uintptr_t _data, void *user return 0; } -VERB(verb_activate, "activate", "[ID]", VERB_ANY, 2, 0, +VERB(verb_activate, "activate", "[ID]\0", VERB_ANY, 2, 0, "Activate a session"); -VERB(verb_activate, "lock-session", "[ID…]", VERB_ANY, VERB_ANY, 0, +VERB(verb_activate, "lock-session", "[ID…]\0", VERB_ANY, VERB_ANY, 0, "Screen lock one or more sessions"); -VERB(verb_activate, "unlock-session", "[ID…]", VERB_ANY, VERB_ANY, 0, +VERB(verb_activate, "unlock-session", "[ID…]\0", VERB_ANY, VERB_ANY, 0, "Screen unlock one or more sessions"); static int verb_activate(int argc, char *argv[], uintptr_t _data, void *userdata) { _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL; @@ -1053,10 +1053,10 @@ static int verb_lock_sessions(int argc, char *argv[], uintptr_t _data, void *use } /* The implementation is above, but we put this here to preserve the logical order in --help. */ -VERB(verb_activate, "terminate-session", "ID…", 2, VERB_ANY, 0, +VERB(verb_activate, "terminate-session", "ID…\0", 2, VERB_ANY, 0, "Terminate one or more sessions"); -VERB(verb_kill_session, "kill-session", "ID…", 2, VERB_ANY, 0, +VERB(verb_kill_session, "kill-session", "ID…\0", 2, VERB_ANY, 0, "Send signal to processes of a session"); static int verb_kill_session(int argc, char *argv[], uintptr_t _data, void *userdata) { _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL; @@ -1163,9 +1163,9 @@ static int verb_list_users(int argc, char *argv[], uintptr_t _data, void *userda return list_table_print(table, "users"); } -VERB(verb_show_user, "user-status", "[USER…]", VERB_ANY, VERB_ANY, 0, +VERB(verb_show_user, "user-status", "[USER…]\0", VERB_ANY, VERB_ANY, 0, "Show user status"); -VERB(verb_show_user, "show-user", "[USER…]", VERB_ANY, VERB_ANY, 0, +VERB(verb_show_user, "show-user", "[USER…]\0", VERB_ANY, VERB_ANY, 0, "Show properties of users or the manager"); static int verb_show_user(int argc, char *argv[], uintptr_t _data, void *userdata) { sd_bus *bus = ASSERT_PTR(userdata); @@ -1218,9 +1218,9 @@ static int verb_show_user(int argc, char *argv[], uintptr_t _data, void *userdat return 0; } -VERB(verb_enable_linger, "enable-linger", "[USER…]", VERB_ANY, VERB_ANY, 0, +VERB(verb_enable_linger, "enable-linger", "[USER…]\0", VERB_ANY, VERB_ANY, 0, "Enable linger state of one or more users"); -VERB(verb_enable_linger, "disable-linger", "[USER…]", VERB_ANY, VERB_ANY, 0, +VERB(verb_enable_linger, "disable-linger", "[USER…]\0", VERB_ANY, VERB_ANY, 0, "Disable linger state of one or more users"); static int verb_enable_linger(int argc, char *argv[], uintptr_t _data, void *userdata) { _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL; @@ -1270,7 +1270,7 @@ static int verb_enable_linger(int argc, char *argv[], uintptr_t _data, void *use return 0; } -VERB(verb_terminate_user, "terminate-user", "USER…", 2, VERB_ANY, 0, +VERB(verb_terminate_user, "terminate-user", "USER…\0", 2, VERB_ANY, 0, "Terminate all sessions of one or more users"); static int verb_terminate_user(int argc, char *argv[], uintptr_t _data, void *userdata) { _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL; @@ -1300,7 +1300,7 @@ static int verb_terminate_user(int argc, char *argv[], uintptr_t _data, void *us return 0; } -VERB(verb_kill_user, "kill-user", "USER…", 2, VERB_ANY, 0, +VERB(verb_kill_user, "kill-user", "USER…\0", 2, VERB_ANY, 0, "Send signal to processes of a user"); static int verb_kill_user(int argc, char *argv[], uintptr_t _data, void *userdata) { _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL; @@ -1385,9 +1385,9 @@ static int verb_list_seats(int argc, char *argv[], uintptr_t _data, void *userda return list_table_print(table, "seats"); } -VERB(verb_show_seat, "seat-status", "[NAME…]", VERB_ANY, VERB_ANY, 0, +VERB(verb_show_seat, "seat-status", "[NAME…]\0", VERB_ANY, VERB_ANY, 0, "Show seat status"); -VERB(verb_show_seat, "show-seat", "[NAME…]", VERB_ANY, VERB_ANY, 0, +VERB(verb_show_seat, "show-seat", "[NAME…]\0", VERB_ANY, VERB_ANY, 0, "Show properties of seats or the manager"); static int verb_show_seat(int argc, char *argv[], uintptr_t _data, void *userdata) { sd_bus *bus = ASSERT_PTR(userdata); @@ -1435,7 +1435,7 @@ static int verb_show_seat(int argc, char *argv[], uintptr_t _data, void *userdat return 0; } -VERB(verb_attach, "attach", "NAME DEVICE…", 3, VERB_ANY, 0, +VERB(verb_attach, "attach", "NAME DEVICE…\0", 3, VERB_ANY, 0, "Attach one or more devices to a seat"); static int verb_attach(int argc, char *argv[], uintptr_t _data, void *userdata) { _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL; @@ -1478,7 +1478,7 @@ static int verb_flush_devices(int argc, char *argv[], uintptr_t _data, void *use return 0; } -VERB(verb_terminate_seat, "terminate-seat", "NAME…", 2, VERB_ANY, 0, +VERB(verb_terminate_seat, "terminate-seat", "NAME…\0", 2, VERB_ANY, 0, "Terminate all sessions on one or more seats"); static int verb_terminate_seat(int argc, char *argv[], uintptr_t _data, void *userdata) { _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL; diff --git a/src/machine/machinectl.c b/src/machine/machinectl.c index 34111e3a5d..716f23c6de 100644 --- a/src/machine/machinectl.c +++ b/src/machine/machinectl.c @@ -702,9 +702,9 @@ static int show_machine_properties(sd_bus *bus, const char *path, bool *new_line return r; } -VERB(verb_show_machine, "status", "NAME…", 2, VERB_ANY, 0, +VERB(verb_show_machine, "status", "NAME…\0", 2, VERB_ANY, 0, "Show VM/container details"); -VERB(verb_show_machine, "show", "[NAME…]", VERB_ANY, VERB_ANY, 0, +VERB(verb_show_machine, "show", "[NAME…]\0", VERB_ANY, VERB_ANY, 0, "Show properties of one or more VMs/containers"); static int verb_show_machine(int argc, char *argv[], uintptr_t _data, void *userdata) { _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL; @@ -782,7 +782,7 @@ static int image_exists(sd_bus *bus, const char *name) { return 1; } -VERB(verb_start_machine, "start", "NAME…", 2, VERB_ANY, 0, +VERB(verb_start_machine, "start", "NAME…\0", 2, VERB_ANY, 0, "Start container as a service"); static int verb_start_machine(int argc, char *argv[], uintptr_t _data, void *userdata) { _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL; @@ -951,7 +951,7 @@ static int on_machine_removed(sd_bus_message *m, void *userdata, sd_bus_error *r return 0; } -VERB(verb_login_machine, "login", "[NAME]", VERB_ANY, 2, 0, +VERB(verb_login_machine, "login", "[NAME]\0", VERB_ANY, 2, 0, "Get a login prompt in a container or on the local host"); static int verb_login_machine(int argc, char *argv[], uintptr_t _data, void *userdata) { _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL; @@ -1004,7 +1004,7 @@ static int verb_login_machine(int argc, char *argv[], uintptr_t _data, void *use return process_forward(event, slot, master, PTY_FORWARD_IGNORE_VHANGUP, machine); } -VERB(verb_shell_machine, "shell", "[[USER@]NAME [COMMAND…]]", VERB_ANY, VERB_ANY, 0, +VERB(verb_shell_machine, "shell", "[[USER@]NAME [COMMAND…]]\0", VERB_ANY, VERB_ANY, 0, "Invoke a shell (or other command) in a container or on the local host"); static int verb_shell_machine(int argc, char *argv[], uintptr_t _data, void *userdata) { _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL, *m = NULL; @@ -1096,9 +1096,9 @@ static int verb_shell_machine(int argc, char *argv[], uintptr_t _data, void *use static int verb_poweroff_machine(int argc, char *argv[], uintptr_t data, void *userdata); -VERB(verb_enable_machine, "enable", "NAME…", 2, VERB_ANY, 0, +VERB(verb_enable_machine, "enable", "NAME…\0", 2, VERB_ANY, 0, "Enable automatic container start at boot"); -VERB(verb_enable_machine, "disable", "NAME…", 2, VERB_ANY, 0, +VERB(verb_enable_machine, "disable", "NAME…\0", 2, VERB_ANY, 0, "Disable automatic container start at boot"); static int verb_enable_machine(int argc, char *argv[], uintptr_t data, void *userdata) { _cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL, *reply = NULL; @@ -1267,9 +1267,9 @@ static int verb_machine_control_one(const char *machine_name, const char *method return 0; } -VERB(verb_poweroff_machine, "poweroff", "NAME…", 2, VERB_ANY, 0, +VERB(verb_poweroff_machine, "poweroff", "NAME…\0", 2, VERB_ANY, 0, "Power off one or more machines"); -VERB(verb_poweroff_machine, "stop", "NAME…", 2, VERB_ANY, 0, +VERB(verb_poweroff_machine, "stop", "NAME…\0", 2, VERB_ANY, 0, /* help= */ NULL); /* Convenience alias */ static int verb_poweroff_machine(int argc, char *argv[], uintptr_t data, void *userdata) { sd_bus *bus = ASSERT_PTR(userdata); @@ -1296,9 +1296,9 @@ static int verb_poweroff_machine(int argc, char *argv[], uintptr_t data, void *u return 0; } -VERB(verb_reboot_machine, "reboot", "NAME…", 2, VERB_ANY, 0, +VERB(verb_reboot_machine, "reboot", "NAME…\0", 2, VERB_ANY, 0, "Reboot one or more machines"); -VERB(verb_reboot_machine, "restart", "NAME…", 2, VERB_ANY, 0, +VERB(verb_reboot_machine, "restart", "NAME…\0", 2, VERB_ANY, 0, /* help= */ NULL); /* Convenience alias */ static int verb_reboot_machine(int argc, char *argv[], uintptr_t data, void *userdata) { sd_bus *bus = ASSERT_PTR(userdata); @@ -1338,19 +1338,19 @@ static int verb_vm_control(int argc, char *argv[], const char *method) { return 0; } -VERB(verb_pause, "pause", "NAME…", 2, VERB_ANY, 0, +VERB(verb_pause, "pause", "NAME…\0", 2, VERB_ANY, 0, "Pause one or more machines"); static int verb_pause(int argc, char *argv[], uintptr_t _data, void *userdata) { return verb_vm_control(argc, argv, "io.systemd.MachineInstance.Pause"); } -VERB(verb_resume, "resume", "NAME…", 2, VERB_ANY, 0, +VERB(verb_resume, "resume", "NAME…\0", 2, VERB_ANY, 0, "Resume one or more paused machines"); static int verb_resume(int argc, char *argv[], uintptr_t _data, void *userdata) { return verb_vm_control(argc, argv, "io.systemd.MachineInstance.Resume"); } -VERB(verb_terminate_machine, "terminate", "NAME…", 2, VERB_ANY, 0, +VERB(verb_terminate_machine, "terminate", "NAME…\0", 2, VERB_ANY, 0, "Terminate one or more machines"); static int verb_terminate_machine(int argc, char *argv[], uintptr_t _data, void *userdata) { sd_bus *bus = ASSERT_PTR(userdata); @@ -1376,7 +1376,7 @@ static int verb_terminate_machine(int argc, char *argv[], uintptr_t _data, void return 0; } -VERB(verb_kill_machine, "kill", "NAME…", 2, VERB_ANY, 0, +VERB(verb_kill_machine, "kill", "NAME…\0", 2, VERB_ANY, 0, "Send signal to processes of a machine"); static int verb_kill_machine(int argc, char *argv[], uintptr_t _data, void *userdata) { _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL; @@ -1410,9 +1410,9 @@ static const char* select_copy_method(bool copy_from, bool force) { return copy_from ? "CopyFromMachine" : "CopyToMachine"; } -VERB(verb_copy_files, "copy-to", "NAME PATH [PATH]", 3, 4, 0, +VERB(verb_copy_files, "copy-to", "NAME PATH [PATH]\0", 3, 4, 0, "Copy files from the host to a container"); -VERB(verb_copy_files, "copy-from", "NAME PATH [PATH]", 3, 4, 0, +VERB(verb_copy_files, "copy-from", "NAME PATH [PATH]\0", 3, 4, 0, "Copy files from a container to the host"); static int verb_copy_files(int argc, char *argv[], uintptr_t _data, void *userdata) { _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL; @@ -1469,7 +1469,7 @@ static int verb_copy_files(int argc, char *argv[], uintptr_t _data, void *userda return 0; } -VERB(verb_bind_mount, "bind", "NAME PATH [PATH]", 3, 4, 0, +VERB(verb_bind_mount, "bind", "NAME PATH [PATH]\0", 3, 4, 0, "Bind mount a path from the host into a container"); static int verb_bind_mount(int argc, char *argv[], uintptr_t _data, void *userdata) { _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL; @@ -1808,9 +1808,9 @@ static int show_image_properties(sd_bus *bus, const char *path, bool *new_line) return r; } -VERB(verb_show_image, "image-status", "[NAME…]", VERB_ANY, VERB_ANY, 0, +VERB(verb_show_image, "image-status", "[NAME…]\0", VERB_ANY, VERB_ANY, 0, "Show image details"); -VERB(verb_show_image, "show-image", "[NAME…]", VERB_ANY, VERB_ANY, 0, +VERB(verb_show_image, "show-image", "[NAME…]\0", VERB_ANY, VERB_ANY, 0, "Show properties of image"); static int verb_show_image(int argc, char *argv[], uintptr_t _data, void *userdata) { _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL; @@ -1896,7 +1896,7 @@ static int get_settings_path(const char *name, char **ret_path) { return -ENOENT; } -VERB(verb_edit_settings, "edit", "NAME|FILE…", 2, VERB_ANY, 0, +VERB(verb_edit_settings, "edit", "NAME|FILE…\0", 2, VERB_ANY, 0, "Edit settings of one or more VMs/containers"); static int verb_edit_settings(int argc, char *argv[], uintptr_t _data, void *userdata) { _cleanup_(edit_file_context_done) EditFileContext context = {}; @@ -1968,7 +1968,7 @@ static int verb_edit_settings(int argc, char *argv[], uintptr_t _data, void *use return do_edit_files_and_install(&context); } -VERB(verb_cat_settings, "cat", "NAME|FILE…", 2, VERB_ANY, 0, +VERB(verb_cat_settings, "cat", "NAME|FILE…\0", 2, VERB_ANY, 0, "Show settings of one or more VMs/containers"); static int verb_cat_settings(int argc, char *argv[], uintptr_t _data, void *userdata) { int r = 0; @@ -2021,7 +2021,7 @@ static int verb_cat_settings(int argc, char *argv[], uintptr_t _data, void *user return r; } -VERB(verb_clone_image, "clone", "NAME NAME", 3, 3, 0, +VERB(verb_clone_image, "clone", "NAME NAME\0", 3, 3, 0, "Clone an image"); static int verb_clone_image(int argc, char *argv[], uintptr_t _data, void *userdata) { _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL; @@ -2047,7 +2047,7 @@ static int verb_clone_image(int argc, char *argv[], uintptr_t _data, void *userd return 0; } -VERB(verb_rename_image, "rename", "NAME NAME", 3, 3, 0, +VERB(verb_rename_image, "rename", "NAME NAME\0", 3, 3, 0, "Rename an image"); static int verb_rename_image(int argc, char *argv[], uintptr_t _data, void *userdata) { _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL; @@ -2069,7 +2069,7 @@ static int verb_rename_image(int argc, char *argv[], uintptr_t _data, void *user return 0; } -VERB(verb_read_only_image, "read-only", "NAME [BOOL]", 2, 3, 0, +VERB(verb_read_only_image, "read-only", "NAME [BOOL]\0", 2, 3, 0, "Mark or unmark image read-only"); static int verb_read_only_image(int argc, char *argv[], uintptr_t _data, void *userdata) { _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL; @@ -2093,7 +2093,7 @@ static int verb_read_only_image(int argc, char *argv[], uintptr_t _data, void *u return 0; } -VERB(verb_remove_image, "remove", "NAME…", 2, VERB_ANY, 0, +VERB(verb_remove_image, "remove", "NAME…\0", 2, VERB_ANY, 0, "Remove an image"); static int verb_remove_image(int argc, char *argv[], uintptr_t _data, void *userdata) { sd_bus *bus = ASSERT_PTR(userdata); @@ -2122,7 +2122,7 @@ static int verb_remove_image(int argc, char *argv[], uintptr_t _data, void *user return 0; } -VERB(verb_set_limit, "set-limit", "[NAME] BYTES", 2, 3, 0, +VERB(verb_set_limit, "set-limit", "[NAME] BYTES\0", 2, 3, 0, "Set image or pool size limit (disk quota)"); static int verb_set_limit(int argc, char *argv[], uintptr_t _data, void *userdata) { _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL; @@ -2210,7 +2210,7 @@ static int verb_clean_images(int argc, char *argv[], uintptr_t _data, void *user return 0; } -VERB(verb_bind_volume, "bind-volume", "MACHINE PROVIDER:VOLUME[:…]", 3, 3, 0, +VERB(verb_bind_volume, "bind-volume", "MACHINE PROVIDER:VOLUME[:…]\0", 3, 3, 0, "Attach a volume to a running machine"); static int verb_bind_volume(int argc, char *argv[], uintptr_t _data, void *userdata) { int r; @@ -2282,7 +2282,7 @@ static int verb_bind_volume(int argc, char *argv[], uintptr_t _data, void *userd return 0; } -VERB(verb_unbind_volume, "unbind-volume", "MACHINE PROVIDER:VOLUME", 3, 3, 0, +VERB(verb_unbind_volume, "unbind-volume", "MACHINE PROVIDER:VOLUME\0", 3, 3, 0, "Detach a volume from a running machine"); static int verb_unbind_volume(int argc, char *argv[], uintptr_t _data, void *userdata) { int r; diff --git a/src/network/networkctl.c b/src/network/networkctl.c index 0268471392..3785664ea6 100644 --- a/src/network/networkctl.c +++ b/src/network/networkctl.c @@ -45,39 +45,39 @@ COMMAND( .pager_flags = &arg_pager_flags, ); -VERB_SCOPE(, verb_list_links, "list", "[PATTERN...]", VERB_ANY, VERB_ANY, VERB_DEFAULT|VERB_ONLINE_ONLY, +VERB_SCOPE(, verb_list_links, "list", "[PATTERN...]\0", VERB_ANY, VERB_ANY, VERB_DEFAULT|VERB_ONLINE_ONLY, "List links"); -VERB_SCOPE(, verb_link_status, "status", "[PATTERN...]", VERB_ANY, VERB_ANY, VERB_ONLINE_ONLY, +VERB_SCOPE(, verb_link_status, "status", "[PATTERN...]\0", VERB_ANY, VERB_ANY, VERB_ONLINE_ONLY, "Show link status"); -VERB_SCOPE(, verb_dhcp_lease, "dhcp-lease", "INTERFACE [CODE[:FORMAT]...]", 2, VERB_ANY, VERB_ONLINE_ONLY, +VERB_SCOPE(, verb_dhcp_lease, "dhcp-lease", "INTERFACE [CODE[:FORMAT]...]\0", 2, VERB_ANY, VERB_ONLINE_ONLY, "Show DHCP lease"); -VERB_SCOPE(, verb_link_lldp_status, "lldp", "[PATTERN...]", VERB_ANY, VERB_ANY, 0, +VERB_SCOPE(, verb_link_lldp_status, "lldp", "[PATTERN...]\0", VERB_ANY, VERB_ANY, 0, "Show LLDP neighbors"); VERB_SCOPE(, verb_list_address_labels, "label", NULL, 1, 1, 0, "Show current address label entries in the kernel"); -VERB_SCOPE(, verb_link_delete, "delete", "DEVICES...", 2, VERB_ANY, 0, +VERB_SCOPE(, verb_link_delete, "delete", "DEVICES...\0", 2, VERB_ANY, 0, "Delete virtual netdevs"); -VERB_SCOPE(, verb_link_varlink_simple_method, "up", "DEVICES...", 2, VERB_ANY, 0, +VERB_SCOPE(, verb_link_varlink_simple_method, "up", "DEVICES...\0", 2, VERB_ANY, 0, "Bring devices up"); -VERB_SCOPE(, verb_link_varlink_simple_method, "down", "DEVICES...", 2, VERB_ANY, 0, +VERB_SCOPE(, verb_link_varlink_simple_method, "down", "DEVICES...\0", 2, VERB_ANY, 0, "Bring devices down"); -VERB_SCOPE(, verb_link_varlink_simple_method, "renew", "DEVICES...", 2, VERB_ANY, VERB_ONLINE_ONLY, +VERB_SCOPE(, verb_link_varlink_simple_method, "renew", "DEVICES...\0", 2, VERB_ANY, VERB_ONLINE_ONLY, "Renew dynamic configurations"); -VERB_SCOPE(, verb_link_varlink_simple_method, "forcerenew", "DEVICES...", 2, VERB_ANY, VERB_ONLINE_ONLY, +VERB_SCOPE(, verb_link_varlink_simple_method, "forcerenew", "DEVICES...\0", 2, VERB_ANY, VERB_ONLINE_ONLY, "Trigger DHCP reconfiguration of all connected clients"); -VERB_SCOPE(, verb_link_varlink_simple_method, "reconfigure", "DEVICES...", 2, VERB_ANY, VERB_ONLINE_ONLY, +VERB_SCOPE(, verb_link_varlink_simple_method, "reconfigure", "DEVICES...\0", 2, VERB_ANY, VERB_ONLINE_ONLY, "Reconfigure interfaces"); VERB_SCOPE(, verb_reload, "reload", NULL, 1, 1, VERB_ONLINE_ONLY, "Reload .network and .netdev files"); -VERB_SCOPE(, verb_edit, "edit", "FILES|DEVICES...", 2, VERB_ANY, 0, +VERB_SCOPE(, verb_edit, "edit", "FILES|DEVICES...\0", 2, VERB_ANY, 0, "Edit network configuration files"); -VERB_SCOPE(, verb_cat, "cat", "[FILES|DEVICES...]", 1, VERB_ANY, 0, +VERB_SCOPE(, verb_cat, "cat", "[FILES|DEVICES...]\0", 1, VERB_ANY, 0, "Show network configuration files"); -VERB_SCOPE(, verb_mask, "mask", "FILES...", 2, VERB_ANY, 0, +VERB_SCOPE(, verb_mask, "mask", "FILES...\0", 2, VERB_ANY, 0, "Mask network configuration files"); -VERB_SCOPE(, verb_unmask, "unmask", "FILES...", 2, VERB_ANY, 0, +VERB_SCOPE(, verb_unmask, "unmask", "FILES...\0", 2, VERB_ANY, 0, "Unmask network configuration files"); -VERB_SCOPE(, verb_persistent_storage, "persistent-storage", "BOOL", 2, 2, 0, +VERB_SCOPE(, verb_persistent_storage, "persistent-storage", "BOOL\0", 2, 2, 0, "Notify systemd-networkd if persistent storage is ready"); VERB_COMMON_HELP_AUTO_HIDDEN(); diff --git a/src/pcrlock/pcrlock.c b/src/pcrlock/pcrlock.c index 8803d087f3..4f5a384072 100644 --- a/src/pcrlock/pcrlock.c +++ b/src/pcrlock/pcrlock.c @@ -4733,7 +4733,7 @@ static int verb_unlock_secureboot_authority(int argc, char *argv[], uintptr_t _d return unlink_pcrlock(PCRLOCK_SECUREBOOT_AUTHORITY_PATH); } -VERB(verb_lock_gpt, "lock-gpt", "[DISK]", VERB_ANY, 2, 0, +VERB(verb_lock_gpt, "lock-gpt", "[DISK]\0", VERB_ANY, 2, 0, "Generate a .pcrlock file from GPT header"); static int verb_lock_gpt(int argc, char *argv[], uintptr_t _data, void *userdata) { _cleanup_(sd_json_variant_unrefp) sd_json_variant *array = NULL, *record = NULL; @@ -4912,7 +4912,7 @@ static int acquire_pe_fd(const char *path) { return acquire_stdin_pe_fd(); } -VERB(verb_lock_pe, "lock-pe", "[BINARY]", VERB_ANY, 2, 0, +VERB(verb_lock_pe, "lock-pe", "[BINARY]\0", VERB_ANY, 2, 0, "Generate a .pcrlock file from PE binary"); static int verb_lock_pe(int argc, char *argv[], uintptr_t _data, void *userdata) { _cleanup_(sd_json_variant_unrefp) sd_json_variant *array = NULL; @@ -4982,7 +4982,7 @@ static void section_hashes_array_done(SectionHashArray *array) { free((*array)[i]); } -VERB(verb_lock_uki, "lock-uki", "[UKI]", VERB_ANY, 2, 0, +VERB(verb_lock_uki, "lock-uki", "[UKI]\0", VERB_ANY, 2, 0, "Generate a .pcrlock file from UKI PE binary"); static int verb_lock_uki(int argc, char *argv[], uintptr_t _data, void *userdata) { _cleanup_(sd_json_variant_unrefp) sd_json_variant *array = NULL, *pe_digests = NULL; @@ -5136,7 +5136,7 @@ static int pcrlock_file_system_path(const char *normalized_path, char **ret) { return 0; } -VERB(verb_lock_file_system, "lock-file-system", "[PATH]", VERB_ANY, 2, 0, +VERB(verb_lock_file_system, "lock-file-system", "[PATH]\0", VERB_ANY, 2, 0, "Generate a .pcrlock file from current root fs + /var/"); static int verb_lock_file_system(int argc, char *argv[], uintptr_t _data, void *userdata) { const char* paths[3] = {}; @@ -5191,7 +5191,7 @@ static int verb_lock_file_system(int argc, char *argv[], uintptr_t _data, void * return 0; } -VERB(verb_unlock_file_system, "unlock-file-system", "[PATH]", VERB_ANY, 2, 0, +VERB(verb_unlock_file_system, "unlock-file-system", "[PATH]\0", VERB_ANY, 2, 0, "Remove .pcrlock file for root fs + /var/"); static int verb_unlock_file_system(int argc, char *argv[], uintptr_t _data, void *userdata) { const char* paths[3] = {}; @@ -5223,7 +5223,7 @@ static int verb_unlock_file_system(int argc, char *argv[], uintptr_t _data, void return 0; } -VERB(verb_lock_kernel_cmdline, "lock-kernel-cmdline", "[FILE]", VERB_ANY, 2, 0, +VERB(verb_lock_kernel_cmdline, "lock-kernel-cmdline", "[FILE]\0", VERB_ANY, 2, 0, "Generate a .pcrlock file from kernel command line"); static int verb_lock_kernel_cmdline(int argc, char *argv[], uintptr_t _data, void *userdata) { _cleanup_(sd_json_variant_unrefp) sd_json_variant *record = NULL, *array = NULL; @@ -5268,7 +5268,7 @@ static int verb_unlock_kernel_cmdline(int argc, char *argv[], uintptr_t _data, v return unlink_pcrlock(PCRLOCK_KERNEL_CMDLINE_PATH); } -VERB(verb_lock_kernel_initrd, "lock-kernel-initrd", "FILE", VERB_ANY, 2, 0, +VERB(verb_lock_kernel_initrd, "lock-kernel-initrd", "FILE\0", VERB_ANY, 2, 0, "Generate a .pcrlock file from an initrd file"); static int verb_lock_kernel_initrd(int argc, char *argv[], uintptr_t _data, void *userdata) { _cleanup_(sd_json_variant_unrefp) sd_json_variant *records = NULL; @@ -5299,7 +5299,7 @@ static int verb_unlock_kernel_initrd(int argc, char *argv[], uintptr_t _data, vo return unlink_pcrlock(PCRLOCK_KERNEL_INITRD_PATH); } -VERB(verb_lock_raw, "lock-raw", "[FILE]", VERB_ANY, 2, 0, +VERB(verb_lock_raw, "lock-raw", "[FILE]\0", VERB_ANY, 2, 0, "Generate a .pcrlock file from raw data"); static int verb_lock_raw(int argc, char *argv[], uintptr_t _data, void *userdata) { _cleanup_(sd_json_variant_unrefp) sd_json_variant *records = NULL; diff --git a/src/portable/portablectl.c b/src/portable/portablectl.c index d544b3022a..2aa0157b2a 100644 --- a/src/portable/portablectl.c +++ b/src/portable/portablectl.c @@ -474,7 +474,7 @@ static bool image_metadata_error_is_no_match(const sd_bus_error *error) { return sd_bus_error_has_name(error, BUS_ERROR_NO_MATCHING_UNIT_FILES); } -VERB(verb_inspect_image, "inspect", "NAME|PATH [PREFIX…]", 2, VERB_ANY, 0, +VERB(verb_inspect_image, "inspect", "NAME|PATH [PREFIX…]\0", 2, VERB_ANY, 0, "Show details of specified portable service image"); static int verb_inspect_image(int argc, char *argv[], uintptr_t _data, void *userdata) { _cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL; @@ -1283,13 +1283,13 @@ static int attach_reattach_image(int argc, char *argv[], const char *method) { return 0; } -VERB(verb_attach_image, "attach", "NAME|PATH [PREFIX…]", 2, VERB_ANY, 0, +VERB(verb_attach_image, "attach", "NAME|PATH [PREFIX…]\0", 2, VERB_ANY, 0, "Attach the specified portable service image"); static int verb_attach_image(int argc, char *argv[], uintptr_t _data, void *userdata) { return attach_reattach_image(argc, argv, strv_isempty(arg_extension_images) && !arg_force ? "AttachImage" : "AttachImageWithExtensions"); } -VERB(verb_detach_image, "detach", "NAME|PATH [PREFIX…]", 2, VERB_ANY, 0, +VERB(verb_detach_image, "detach", "NAME|PATH [PREFIX…]\0", 2, VERB_ANY, 0, "Detach the specified portable service image"); static int verb_detach_image(int argc, char *argv[], uintptr_t _data, void *userdata) { _cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL, *reply = NULL; @@ -1345,13 +1345,13 @@ static int verb_detach_image(int argc, char *argv[], uintptr_t _data, void *user return 0; } -VERB(verb_reattach_image, "reattach", "NAME|PATH [PREFIX…]", 2, VERB_ANY, 0, +VERB(verb_reattach_image, "reattach", "NAME|PATH [PREFIX…]\0", 2, VERB_ANY, 0, "Reattach the specified portable service image"); static int verb_reattach_image(int argc, char *argv[], uintptr_t _data, void *userdata) { return attach_reattach_image(argc, argv, strv_isempty(arg_extension_images) && !arg_force ? "ReattachImage" : "ReattachImageWithExtensions"); } -VERB(verb_is_image_attached, "is-attached", "NAME|PATH", 2, 2, 0, +VERB(verb_is_image_attached, "is-attached", "NAME|PATH\0", 2, 2, 0, "Query if portable service image is attached"); static int verb_is_image_attached(int argc, char *argv[], uintptr_t _data, void *userdata) { _cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL, *reply = NULL; @@ -1403,7 +1403,7 @@ static int verb_is_image_attached(int argc, char *argv[], uintptr_t _data, void return streq(state, "detached"); } -VERB(verb_read_only_image, "read-only", "NAME|PATH [BOOL]", 2, 3, 0, +VERB(verb_read_only_image, "read-only", "NAME|PATH [BOOL]\0", 2, 3, 0, "Mark or unmark portable service image read-only"); static int verb_read_only_image(int argc, char *argv[], uintptr_t _data, void *userdata) { _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL; @@ -1434,7 +1434,7 @@ static int verb_read_only_image(int argc, char *argv[], uintptr_t _data, void *u return 0; } -VERB(verb_remove_image, "remove", "NAME|PATH…", 2, VERB_ANY, 0, +VERB(verb_remove_image, "remove", "NAME|PATH…\0", 2, VERB_ANY, 0, "Remove a portable service image"); static int verb_remove_image(int argc, char *argv[], uintptr_t _data, void *userdata) { _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL; @@ -1472,7 +1472,7 @@ static int verb_remove_image(int argc, char *argv[], uintptr_t _data, void *user return 0; } -VERB(verb_set_limit, "set-limit", "[NAME|PATH] LIMIT", 2, 3, 0, +VERB(verb_set_limit, "set-limit", "[NAME|PATH] LIMIT\0", 2, 3, 0, "Set image or pool size limit (disk quota)"); static int verb_set_limit(int argc, char *argv[], uintptr_t _data, void *userdata) { _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL; diff --git a/src/report/report.c b/src/report/report.c index bb4b0329d6..f7c0c35207 100644 --- a/src/report/report.c +++ b/src/report/report.c @@ -650,13 +650,13 @@ static int context_collect_metrics(Context *context) { return 1; } -VERB_FULL(verb_metrics, "metrics", "[MATCH…]", VERB_ANY, VERB_ANY, 0, ACTION_LIST_METRICS, +VERB_FULL(verb_metrics, "metrics", "[MATCH…]\0", VERB_ANY, VERB_ANY, 0, ACTION_LIST_METRICS, "Acquire list of metrics and their values"); -VERB_FULL(verb_metrics, "describe", "[MATCH…]", VERB_ANY, VERB_ANY, 0, ACTION_DESCRIBE_METRICS, +VERB_FULL(verb_metrics, "describe", "[MATCH…]\0", VERB_ANY, VERB_ANY, 0, ACTION_DESCRIBE_METRICS, "Describe available metrics"); -VERB_FULL(verb_metrics, "generate", "[MATCH…]", VERB_ANY, VERB_ANY, 0, ACTION_GENERATE, +VERB_FULL(verb_metrics, "generate", "[MATCH…]\0", VERB_ANY, VERB_ANY, 0, ACTION_GENERATE, "Build a report with metrics"); -VERB_FULL(verb_metrics, "upload", "[MATCH…]", VERB_ANY, VERB_ANY, 0, ACTION_UPLOAD, +VERB_FULL(verb_metrics, "upload", "[MATCH…]\0", VERB_ANY, VERB_ANY, 0, ACTION_UPLOAD, "Upload a report with metrics"); static int verb_metrics(int argc, char *argv[], uintptr_t data, void *userdata) { Action action = data; diff --git a/src/resolve/resolvectl.c b/src/resolve/resolvectl.c index aa8df3cdfb..149f229a7d 100644 --- a/src/resolve/resolvectl.c +++ b/src/resolve/resolvectl.c @@ -881,7 +881,7 @@ invalid: "Invalid DNS URI: %s", name); } -VERB(verb_query, "query", "HOSTNAME|ADDRESS…", 2, VERB_ANY, 0, +VERB(verb_query, "query", "HOSTNAME|ADDRESS…\0", 2, VERB_ANY, 0, "Resolve domain names, IPv4 and IPv6 addresses"); static int verb_query(int argc, char *argv[], uintptr_t _data, void *userdata) { int ret = 0, r; @@ -1025,7 +1025,7 @@ static int resolve_service(const char *name, const char *type, const char *domai return 0; } -VERB(verb_service, "service", "[[NAME] TYPE] DOMAIN", 2, 4, 0, +VERB(verb_service, "service", "[[NAME] TYPE] DOMAIN\0", 2, 4, 0, "Resolve service (SRV)"); static int verb_service(int argc, char *argv[], uintptr_t _data, void *userdata) { uint64_t flags = arg_flags; @@ -1104,7 +1104,7 @@ static int resolve_openpgp(const char *address) { } #endif -VERB(verb_openpgp, "openpgp", "EMAIL@DOMAIN…", 2, VERB_ANY, 0, +VERB(verb_openpgp, "openpgp", "EMAIL@DOMAIN…\0", 2, VERB_ANY, 0, "Query OpenPGP public key"); static int verb_openpgp(int argc, char *argv[], uintptr_t _data, void *userdata) { #if HAVE_OPENSSL @@ -1157,7 +1157,7 @@ static bool service_family_is_valid(const char *s) { return STR_IN_SET(s, "tcp", "udp", "sctp"); } -VERB(verb_tlsa, "tlsa", "DOMAIN[:PORT]…", 2, VERB_ANY, 0, +VERB(verb_tlsa, "tlsa", "DOMAIN[:PORT]…\0", 2, VERB_ANY, 0, "Query TLS public key"); static int verb_tlsa(int argc, char *argv[], uintptr_t _data, void *userdata) { const char *family = "tcp"; @@ -1770,7 +1770,7 @@ static int status_ifindex(int ifindex, StatusMode mode) { return status_full(mode, STRV_MAKE(ifname)); } -VERB(verb_status, "status", "[LINK…]", VERB_ANY, VERB_ANY, VERB_DEFAULT, +VERB(verb_status, "status", "[LINK…]\0", VERB_ANY, VERB_ANY, VERB_DEFAULT, "Show link and server status"); static int verb_status(int argc, char *argv[], uintptr_t _data, void *userdata) { return status_full(STATUS_ALL, strv_skip(argv, 1)); @@ -2658,7 +2658,7 @@ static int verb_show_server_state(int argc, char *argv[], uintptr_t _data, void return sd_json_variant_dump(d, arg_json_format_flags, NULL, NULL); } -VERB(verb_dns, "dns", "[LINK [SERVER…]]", VERB_ANY, VERB_ANY, 0, +VERB(verb_dns, "dns", "[LINK [SERVER…]]\0", VERB_ANY, VERB_ANY, 0, "Get/set per-interface DNS server address"); static int verb_dns(int argc, char *argv[], uintptr_t _data, void *userdata) { _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL; @@ -2745,7 +2745,7 @@ static int call_domain(sd_bus *bus, char **domain, const BusLocator *locator, sd return sd_bus_call(bus, req, 0, error, NULL); } -VERB(verb_domain, "domain", "[LINK [DOMAIN…]]", VERB_ANY, VERB_ANY, 0, +VERB(verb_domain, "domain", "[LINK [DOMAIN…]]\0", VERB_ANY, VERB_ANY, 0, "Get/set per-interface search domain"); static int verb_domain(int argc, char *argv[], uintptr_t _data, void *userdata) { _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL; @@ -2786,7 +2786,7 @@ static int verb_domain(int argc, char *argv[], uintptr_t _data, void *userdata) return 0; } -VERB(verb_default_route, "default-route", "[LINK [BOOL]]", VERB_ANY, 3, 0, +VERB(verb_default_route, "default-route", "[LINK [BOOL]]\0", VERB_ANY, 3, 0, "Get/set per-interface default route flag"); static int verb_default_route(int argc, char *argv[], uintptr_t _data, void *userdata) { _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL; @@ -2832,7 +2832,7 @@ static int verb_default_route(int argc, char *argv[], uintptr_t _data, void *use return 0; } -VERB(verb_llmnr, "llmnr", "[LINK [MODE]]", VERB_ANY, 3, 0, +VERB(verb_llmnr, "llmnr", "[LINK [MODE]]\0", VERB_ANY, 3, 0, "Get/set per-interface LLMNR mode"); static int verb_llmnr(int argc, char *argv[], uintptr_t _data, void *userdata) { _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL; @@ -2892,7 +2892,7 @@ static int verb_llmnr(int argc, char *argv[], uintptr_t _data, void *userdata) { return 0; } -VERB(verb_mdns, "mdns", "[LINK [MODE]]", VERB_ANY, 3, 0, +VERB(verb_mdns, "mdns", "[LINK [MODE]]\0", VERB_ANY, 3, 0, "Get/set per-interface MulticastDNS mode"); static int verb_mdns(int argc, char *argv[], uintptr_t _data, void *userdata) { _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL; @@ -2958,7 +2958,7 @@ static int verb_mdns(int argc, char *argv[], uintptr_t _data, void *userdata) { return 0; } -VERB(verb_dns_over_tls, "dnsovertls", "[LINK [MODE]]", VERB_ANY, 3, 0, +VERB(verb_dns_over_tls, "dnsovertls", "[LINK [MODE]]\0", VERB_ANY, 3, 0, "Get/set per-interface DNS-over-TLS mode"); static int verb_dns_over_tls(int argc, char *argv[], uintptr_t _data, void *userdata) { _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL; @@ -3006,7 +3006,7 @@ static int verb_dns_over_tls(int argc, char *argv[], uintptr_t _data, void *user return 0; } -VERB(verb_dnssec, "dnssec", "[LINK [MODE]]", VERB_ANY, 3, 0, +VERB(verb_dnssec, "dnssec", "[LINK [MODE]]\0", VERB_ANY, 3, 0, "Get/set per-interface DNSSEC mode"); static int verb_dnssec(int argc, char *argv[], uintptr_t _data, void *userdata) { _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL; @@ -3069,7 +3069,7 @@ static int call_nta(sd_bus *bus, char **nta, const BusLocator *locator, sd_bus_ return sd_bus_call(bus, req, 0, error, NULL); } -VERB(verb_nta, "nta", "[LINK [DOMAIN…]]", VERB_ANY, VERB_ANY, 0, +VERB(verb_nta, "nta", "[LINK [DOMAIN…]]\0", VERB_ANY, VERB_ANY, 0, "Get/set per-interface DNSSEC NTA"); static int verb_nta(int argc, char *argv[], uintptr_t _data, void *userdata) { _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL; @@ -3129,7 +3129,7 @@ static int verb_nta(int argc, char *argv[], uintptr_t _data, void *userdata) { return 0; } -VERB(verb_revert_link, "revert", "LINK", VERB_ANY, 2, 0, +VERB(verb_revert_link, "revert", "LINK\0", VERB_ANY, 2, 0, "Revert per-interface configuration"); static int verb_revert_link(int argc, char *argv[], uintptr_t _data, void *userdata) { _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL; @@ -3168,7 +3168,7 @@ static int verb_revert_link(int argc, char *argv[], uintptr_t _data, void *userd return 0; } -VERB(verb_log_level, "log-level", "[LEVEL]", VERB_ANY, 2, 0, +VERB(verb_log_level, "log-level", "[LEVEL]\0", VERB_ANY, 2, 0, "Get/set logging threshold for systemd-resolved"); static int verb_log_level(int argc, char *argv[], uintptr_t _data, void *userdata) { int r; diff --git a/src/sbsign/sbsign.c b/src/sbsign/sbsign.c index e6729dade2..f5c6b96b68 100644 --- a/src/sbsign/sbsign.c +++ b/src/sbsign/sbsign.c @@ -365,7 +365,7 @@ static int pkcs7_add_digest_attribute(PKCS7 *p7, BIO *data, PKCS7_SIGNER_INFO *s return 0; } -VERB(verb_sign, "sign", "EXEFILE", 2, 2, 0, +VERB(verb_sign, "sign", "EXEFILE\0", 2, 2, 0, "Sign the given binary for EFI Secure Boot"); static int verb_sign(int argc, char *argv[], uintptr_t _data, void *userdata) { _cleanup_(openssl_ask_password_ui_freep) OpenSSLAskPasswordUI *ui = NULL; diff --git a/src/shared/verbs.c b/src/shared/verbs.c index b9b0c40a76..ddc11cc004 100644 --- a/src/shared/verbs.c +++ b/src/shared/verbs.c @@ -176,40 +176,54 @@ static int verb_add_help_one(Table *table, const Verb *verb) { assert(verb); bool is_default = FLAGS_SET(verb->flags, VERB_DEFAULT); + _cleanup_free_ char *cell = NULL; int r; - /* 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. */ - _cleanup_free_ char *s = strjoin(" ", - is_default ? "[" : "", - verb->verb, - verb->argspec ? " " : "", - strempty(verb->argspec), - is_default ? "]" : ""); - if (!s) - return log_oom(); + /* The synopsis cell contains one line per entry in the argspec nulstr. An unset argspec is + * equivalent to a single empty specification. */ + for (const char *spec = empty_to_null(verb->argspec);;) { + /* 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. */ + _cleanup_free_ char *s = strjoin(" ", + is_default ? "[" : "", + verb->verb, + spec ? " " : "", + strempty(spec), + is_default ? "]" : ""); + if (!s) + return log_oom(); - const char *ss = NULL; - if (columns() < VERB_SYNOPSIS_WIDTH_SANE * 4) { - /* If the synopsis is very wide, try to split it up. But do this only if the terminal - * is not very wide. If it _is_ wide, the broken up synopsis would look silly. */ - const char *p = find_point_to_break(s, VERB_SYNOPSIS_WIDTH_SANE), *p2 = NULL; - if (p) { - const char *s1 = strndupa_safe(s, p - s), *s2 = NULL; + const char *ss = NULL; + if (columns() < VERB_SYNOPSIS_WIDTH_SANE * 4) { + /* If the synopsis is very wide, try to split it up. But do this only if the terminal + * is not very wide. If it _is_ wide, the broken up synopsis would look silly. */ + const char *p = find_point_to_break(s, VERB_SYNOPSIS_WIDTH_SANE), *p2 = NULL; + if (p) { + const char *s1 = strndupa_safe(s, p - s), *s2 = NULL; - p2 = find_point_to_break(p, VERB_SYNOPSIS_WIDTH_SANE - 4); /* we indent by two spaces more */ - if (p2) - s2 = strndupa_safe(p, p2 - p); + p2 = find_point_to_break(p, VERB_SYNOPSIS_WIDTH_SANE - 4); /* we indent by two spaces more */ + if (p2) + s2 = strndupa_safe(p, p2 - p); - if (s2) - ss = strjoina(s1, "\n ", s2, "\n ", p2); - else - ss = strjoina(s1, "\n ", p); + if (s2) + ss = strjoina(s1, "\n ", s2, "\n ", p2); + else + ss = strjoina(s1, "\n ", p); + } } + + if (!strextend_with_separator(&cell, "\n", ss ?: s)) + return log_oom(); + + if (!spec) + break; + spec += strlen(spec) + 1; /* Advance to the next entry in the nulstr */ + if (isempty(spec)) + break; } - r = table_add_cell(table, NULL, TABLE_STRING, ss ?: s); + r = table_add_cell(table, NULL, TABLE_STRING, cell); if (r < 0) return table_log_add_error(r); diff --git a/src/shared/verbs.h b/src/shared/verbs.h index b8a990a62b..cda8db56d3 100644 --- a/src/shared/verbs.h +++ b/src/shared/verbs.h @@ -18,7 +18,7 @@ typedef enum CommandFlags { typedef struct CommandDescription { const char *names; /* nulstr with the main command name and potentially aliases */ const char *abstract; /* concrete abstract */ - const char *argspec; /* optional specification of positional args in synopsis */ + const char *argspec; /* optional nulstr with specifications of positional args */ const char *footer; /* optional footer to print right above man page links */ const char *man_pages; /* nulstr with man page names */ const char *option_namespace; /* optional option namespace for this command */ @@ -41,7 +41,7 @@ typedef struct _alignptr_ Verb { VerbFlags flags; int (* const dispatch)(int argc, char *argv[], uintptr_t data, void *userdata); uintptr_t data; - const char *argspec; + const char *argspec; /* optional nulstr with specifications of positional args */ const char *help; const char *option_namespace; /* optional namespace with the verb's own options */ } Verb; diff --git a/src/storage/storagectl.c b/src/storage/storagectl.c index f186603de9..3c11391f15 100644 --- a/src/storage/storagectl.c +++ b/src/storage/storagectl.c @@ -140,7 +140,7 @@ static int on_list_reply( return 0; } -VERB(verb_list_volumes, "volumes", "GLOB", /* min_args= */ VERB_ANY, /* max_args= */ 2, VERB_DEFAULT, "List storage volumes"); +VERB(verb_list_volumes, "volumes", "GLOB\0", /* min_args= */ VERB_ANY, /* max_args= */ 2, VERB_DEFAULT, "List storage volumes"); static int verb_list_volumes(int argc, char *argv[], uintptr_t data, void *userdata) { int r; @@ -245,7 +245,7 @@ static int on_list_templates_reply( return 0; } -VERB(verb_templates, "templates", "GLOB", /* min_args= */ VERB_ANY, /* max_args= */ 2, /* flags= */ 0, "List storage volume templates"); +VERB(verb_templates, "templates", "GLOB\0", /* min_args= */ VERB_ANY, /* max_args= */ 2, /* flags= */ 0, "List storage volume templates"); static int verb_templates(int argc, char *argv[], uintptr_t data, void *userdata) { int r; diff --git a/src/systemctl/systemctl.c b/src/systemctl/systemctl.c index 2e4f50d086..18026e1e52 100644 --- a/src/systemctl/systemctl.c +++ b/src/systemctl/systemctl.c @@ -911,128 +911,128 @@ int systemctl_dispatch_parse_argv(int argc, char *argv[], int log_level_shift, c VERB_GROUP("Unit Commands"); -VERB_SCOPE(, verb_list_units, "list-units", "[PATTERN…]", VERB_ANY, VERB_ANY, VERB_DEFAULT|VERB_ONLINE_ONLY, +VERB_SCOPE(, verb_list_units, "list-units", "[PATTERN…]\0", VERB_ANY, VERB_ANY, VERB_DEFAULT|VERB_ONLINE_ONLY, "List units currently in memory"); -VERB_SCOPE(, verb_list_automounts, "list-automounts", "[PATTERN…]", VERB_ANY, VERB_ANY, VERB_ONLINE_ONLY, +VERB_SCOPE(, verb_list_automounts, "list-automounts", "[PATTERN…]\0", VERB_ANY, VERB_ANY, VERB_ONLINE_ONLY, "List automount units currently in memory, ordered by path"); -VERB_SCOPE(, verb_list_paths, "list-paths", "[PATTERN…]", VERB_ANY, VERB_ANY, VERB_ONLINE_ONLY, +VERB_SCOPE(, verb_list_paths, "list-paths", "[PATTERN…]\0", VERB_ANY, VERB_ANY, VERB_ONLINE_ONLY, "List path units currently in memory, ordered by path"); -VERB_SCOPE(, verb_list_sockets, "list-sockets", "[PATTERN…]", VERB_ANY, VERB_ANY, VERB_ONLINE_ONLY, +VERB_SCOPE(, verb_list_sockets, "list-sockets", "[PATTERN…]\0", VERB_ANY, VERB_ANY, VERB_ONLINE_ONLY, "List socket units currently in memory, ordered by address"); -VERB_SCOPE(, verb_list_timers, "list-timers", "[PATTERN…]", VERB_ANY, VERB_ANY, VERB_ONLINE_ONLY, +VERB_SCOPE(, verb_list_timers, "list-timers", "[PATTERN…]\0", VERB_ANY, VERB_ANY, VERB_ONLINE_ONLY, "List timer units currently in memory, ordered by next elapse"); -VERB_SCOPE(, verb_is_active, "is-active", "PATTERN…", 2, VERB_ANY, VERB_ONLINE_ONLY, +VERB_SCOPE(, verb_is_active, "is-active", "PATTERN…\0", 2, VERB_ANY, VERB_ONLINE_ONLY, "Check whether units are active"); -VERB_SCOPE(, verb_is_failed, "is-failed", "[PATTERN…]", VERB_ANY, VERB_ANY, VERB_ONLINE_ONLY, +VERB_SCOPE(, verb_is_failed, "is-failed", "[PATTERN…]\0", VERB_ANY, VERB_ANY, VERB_ONLINE_ONLY, "Check whether units are failed or system is in degraded state"); -VERB_SCOPE(, verb_show, "status", "[PATTERN…|PID…]", VERB_ANY, VERB_ANY, VERB_ONLINE_ONLY, +VERB_SCOPE(, verb_show, "status", "[PATTERN…|PID…]\0", VERB_ANY, VERB_ANY, VERB_ONLINE_ONLY, "Show runtime status of one or more units"); -VERB_SCOPE(, verb_show, "show", "[PATTERN…|JOB…]", VERB_ANY, VERB_ANY, VERB_ONLINE_ONLY, +VERB_SCOPE(, verb_show, "show", "[PATTERN…|JOB…]\0", VERB_ANY, VERB_ANY, VERB_ONLINE_ONLY, "Show properties of one or more units/jobs or the manager"); -VERB_SCOPE(, verb_cat, "cat", "PATTERN…", 2, VERB_ANY, VERB_ONLINE_ONLY, +VERB_SCOPE(, verb_cat, "cat", "PATTERN…\0", 2, VERB_ANY, VERB_ONLINE_ONLY, "Show files and drop-ins of specified units"); -VERB_SCOPE(, verb_show, "help", "PATTERN…|PID…", VERB_ANY, VERB_ANY, VERB_ONLINE_ONLY, +VERB_SCOPE(, verb_show, "help", "PATTERN…|PID…\0", VERB_ANY, VERB_ANY, VERB_ONLINE_ONLY, "Show manual for one or more units"); -VERB_SCOPE(, verb_list_dependencies, "list-dependencies", "[UNIT…]", VERB_ANY, VERB_ANY, VERB_ONLINE_ONLY, +VERB_SCOPE(, verb_list_dependencies, "list-dependencies", "[UNIT…]\0", VERB_ANY, VERB_ANY, VERB_ONLINE_ONLY, "Recursively show units which are required or wanted by the units " "or by which those units are required or wanted"); -VERB_SCOPE(, verb_start, "start", "UNIT…", 2, VERB_ANY, VERB_ONLINE_ONLY, +VERB_SCOPE(, verb_start, "start", "UNIT…\0", 2, VERB_ANY, VERB_ONLINE_ONLY, "Start (activate) one or more units"); -VERB_SCOPE(, verb_start, "stop", "UNIT…", 2, VERB_ANY, VERB_ONLINE_ONLY, +VERB_SCOPE(, verb_start, "stop", "UNIT…\0", 2, VERB_ANY, VERB_ONLINE_ONLY, "Stop (deactivate) one or more units"); -VERB_SCOPE(, verb_start, "reload", "UNIT…", 2, VERB_ANY, VERB_ONLINE_ONLY, +VERB_SCOPE(, verb_start, "reload", "UNIT…\0", 2, VERB_ANY, VERB_ONLINE_ONLY, "Reload one or more units"); -VERB_SCOPE(, verb_start, "restart", "UNIT…", 2, VERB_ANY, VERB_ONLINE_ONLY, +VERB_SCOPE(, verb_start, "restart", "UNIT…\0", 2, VERB_ANY, VERB_ONLINE_ONLY, "Start or restart one or more units"); -VERB_SCOPE(, verb_start, "try-restart", "UNIT…", 2, VERB_ANY, VERB_ONLINE_ONLY, +VERB_SCOPE(, verb_start, "try-restart", "UNIT…\0", 2, VERB_ANY, VERB_ONLINE_ONLY, "Restart one or more units if active"); VERB_SCOPE(, verb_start, "enqueue-marked", NULL, 1, 1, VERB_ONLINE_ONLY, "Enqueue jobs for all marked units"); -VERB_SCOPE(, verb_start, "reload-or-restart", "UNIT…", VERB_ANY, VERB_ANY, VERB_ONLINE_ONLY, +VERB_SCOPE(, verb_start, "reload-or-restart", "UNIT…\0", VERB_ANY, VERB_ANY, VERB_ONLINE_ONLY, "Reload one or more units if possible, otherwise start or restart"); -VERB_SCOPE(, verb_start, "try-reload-or-restart", "UNIT…", 2, VERB_ANY, VERB_ONLINE_ONLY, +VERB_SCOPE(, verb_start, "try-reload-or-restart", "UNIT…\0", 2, VERB_ANY, VERB_ONLINE_ONLY, "If active, reload one or more units, if supported, otherwise restart"); -VERB_SCOPE(, verb_start, "isolate", "UNIT", 2, 2, VERB_ONLINE_ONLY, +VERB_SCOPE(, verb_start, "isolate", "UNIT\0", 2, 2, VERB_ONLINE_ONLY, "Start one unit and stop all others"); -VERB_SCOPE(, verb_kill, "kill", "UNIT…", 2, VERB_ANY, VERB_ONLINE_ONLY, +VERB_SCOPE(, verb_kill, "kill", "UNIT…\0", 2, VERB_ANY, VERB_ONLINE_ONLY, "Send signal to processes of a unit"); -VERB_SCOPE(, verb_clean_or_freeze, "clean", "UNIT…", 2, VERB_ANY, VERB_ONLINE_ONLY, +VERB_SCOPE(, verb_clean_or_freeze, "clean", "UNIT…\0", 2, VERB_ANY, VERB_ONLINE_ONLY, "Clean runtime, cache, state, logs or configuration of unit"); -VERB_SCOPE(, verb_clean_or_freeze, "freeze", "PATTERN…", 2, VERB_ANY, VERB_ONLINE_ONLY, +VERB_SCOPE(, verb_clean_or_freeze, "freeze", "PATTERN…\0", 2, VERB_ANY, VERB_ONLINE_ONLY, "Freeze execution of unit processes"); -VERB_SCOPE(, verb_clean_or_freeze, "thaw", "PATTERN…", 2, VERB_ANY, VERB_ONLINE_ONLY, +VERB_SCOPE(, verb_clean_or_freeze, "thaw", "PATTERN…\0", 2, VERB_ANY, VERB_ONLINE_ONLY, "Resume execution of a frozen unit"); -VERB_SCOPE(, verb_set_property, "set-property", "UNIT PROPERTY=VALUE…", 3, VERB_ANY, VERB_ONLINE_ONLY, +VERB_SCOPE(, verb_set_property, "set-property", "UNIT PROPERTY=VALUE…\0", 3, VERB_ANY, VERB_ONLINE_ONLY, "Sets one or more properties of a unit"); -VERB_SCOPE(, verb_bind, "bind", "UNIT PATH [PATH]", 3, 4, VERB_ONLINE_ONLY, +VERB_SCOPE(, verb_bind, "bind", "UNIT PATH [PATH]\0", 3, 4, VERB_ONLINE_ONLY, "Bind-mount a path from the host into a unit's namespace"); -VERB_SCOPE(, verb_mount_image, "mount-image", "UNIT PATH [PATH [OPTS]]", 4, 5, VERB_ONLINE_ONLY, +VERB_SCOPE(, verb_mount_image, "mount-image", "UNIT PATH [PATH [OPTS]]\0", 4, 5, VERB_ONLINE_ONLY, "Mount an image from the host into a unit's namespace"); -VERB_SCOPE(, verb_service_log_setting, "service-log-level", "SERVICE [LEVEL]", 2, 3, VERB_ONLINE_ONLY, +VERB_SCOPE(, verb_service_log_setting, "service-log-level", "SERVICE [LEVEL]\0", 2, 3, VERB_ONLINE_ONLY, "Get/set logging threshold for service"); -VERB_SCOPE(, verb_service_log_setting, "service-log-target", "SERVICE [TARGET]", 2, 3, VERB_ONLINE_ONLY, +VERB_SCOPE(, verb_service_log_setting, "service-log-target", "SERVICE [TARGET]\0", 2, 3, VERB_ONLINE_ONLY, "Get/set logging target for service"); -VERB_SCOPE(, verb_reset_failed, "reset-failed", "[PATTERN…]", VERB_ANY, VERB_ANY, VERB_ONLINE_ONLY, +VERB_SCOPE(, verb_reset_failed, "reset-failed", "[PATTERN…]\0", VERB_ANY, VERB_ANY, VERB_ONLINE_ONLY, "Reset failed state for all, one, or more units"); -VERB_SCOPE(, verb_whoami, "whoami", "[PID…]", VERB_ANY, VERB_ANY, VERB_ONLINE_ONLY, +VERB_SCOPE(, verb_whoami, "whoami", "[PID…]\0", VERB_ANY, VERB_ANY, VERB_ONLINE_ONLY, "Return unit caller or specified PIDs are part of"); VERB_GROUP("Unit File Commands"); -VERB_SCOPE(, verb_list_unit_files, "list-unit-files", "[PATTERN…]", VERB_ANY, VERB_ANY, 0, +VERB_SCOPE(, verb_list_unit_files, "list-unit-files", "[PATTERN…]\0", VERB_ANY, VERB_ANY, 0, "List installed unit files"); -VERB_SCOPE(, verb_enable, "enable", "[UNIT…|PATH…]", 2, VERB_ANY, 0, +VERB_SCOPE(, verb_enable, "enable", "[UNIT…|PATH…]\0", 2, VERB_ANY, 0, "Enable one or more unit files"); -VERB_SCOPE(, verb_enable, "disable", "UNIT…", 2, VERB_ANY, 0, +VERB_SCOPE(, verb_enable, "disable", "UNIT…\0", 2, VERB_ANY, 0, "Disable one or more unit files"); -VERB_SCOPE(, verb_enable, "reenable", "UNIT…", 2, VERB_ANY, 0, +VERB_SCOPE(, verb_enable, "reenable", "UNIT…\0", 2, VERB_ANY, 0, "Reenable one or more unit files"); -VERB_SCOPE(, verb_enable, "preset", "UNIT…", 2, VERB_ANY, 0, +VERB_SCOPE(, verb_enable, "preset", "UNIT…\0", 2, VERB_ANY, 0, "Enable/disable one or more unit files based on preset configuration"); VERB_SCOPE(, verb_preset_all, "preset-all", NULL, VERB_ANY, 1, 0, "Enable/disable all unit files based on preset configuration"); -VERB_SCOPE(, verb_is_enabled, "is-enabled", "UNIT…", 2, VERB_ANY, 0, +VERB_SCOPE(, verb_is_enabled, "is-enabled", "UNIT…\0", 2, VERB_ANY, 0, "Check whether unit files are enabled"); -VERB_SCOPE(, verb_enable, "mask", "UNIT…", 2, VERB_ANY, 0, +VERB_SCOPE(, verb_enable, "mask", "UNIT…\0", 2, VERB_ANY, 0, "Mask one or more units"); -VERB_SCOPE(, verb_enable, "unmask", "UNIT…", 2, VERB_ANY, 0, +VERB_SCOPE(, verb_enable, "unmask", "UNIT…\0", 2, VERB_ANY, 0, "Unmask one or more units"); -VERB_SCOPE(, verb_enable, "link", "PATH…", 2, VERB_ANY, 0, +VERB_SCOPE(, verb_enable, "link", "PATH…\0", 2, VERB_ANY, 0, "Link one or more units files into the search path"); -VERB_SCOPE(, verb_enable, "revert", "UNIT…", 2, VERB_ANY, 0, +VERB_SCOPE(, verb_enable, "revert", "UNIT…\0", 2, VERB_ANY, 0, "Revert one or more unit files to vendor version"); -VERB_SCOPE(, verb_add_dependency, "add-wants", "TARGET UNIT…", 3, VERB_ANY, 0, +VERB_SCOPE(, verb_add_dependency, "add-wants", "TARGET UNIT…\0", 3, VERB_ANY, 0, "Add 'Wants' dependency for the target on specified one or more units"); -VERB_SCOPE(, verb_add_dependency, "add-requires", "TARGET UNIT…", 3, VERB_ANY, 0, +VERB_SCOPE(, verb_add_dependency, "add-requires", "TARGET UNIT…\0", 3, VERB_ANY, 0, "Add 'Requires' dependency for the target on specified one or more units"); -VERB_SCOPE(, verb_edit, "edit", "UNIT…", 2, VERB_ANY, VERB_ONLINE_ONLY, +VERB_SCOPE(, verb_edit, "edit", "UNIT…\0", 2, VERB_ANY, VERB_ONLINE_ONLY, "Edit one or more unit files"); VERB_SCOPE(, verb_get_default, "get-default", NULL, VERB_ANY, 1, 0, "Get the name of the default target"); -VERB_SCOPE(, verb_set_default, "set-default", "TARGET", 2, 2, 0, +VERB_SCOPE(, verb_set_default, "set-default", "TARGET\0", 2, 2, 0, "Set the default target"); VERB_GROUP("Machine Commands"); -VERB_SCOPE(, verb_list_machines, "list-machines", "[PATTERN…]", VERB_ANY, VERB_ANY, VERB_ONLINE_ONLY, +VERB_SCOPE(, verb_list_machines, "list-machines", "[PATTERN…]\0", VERB_ANY, VERB_ANY, VERB_ONLINE_ONLY, "List local containers and host"); VERB_GROUP("Job Commands"); -VERB_SCOPE(, verb_list_jobs, "list-jobs", "[PATTERN…]", VERB_ANY, VERB_ANY, VERB_ONLINE_ONLY, +VERB_SCOPE(, verb_list_jobs, "list-jobs", "[PATTERN…]\0", VERB_ANY, VERB_ANY, VERB_ONLINE_ONLY, "List jobs"); -VERB_SCOPE(, verb_cancel, "cancel", "[JOB…]", VERB_ANY, VERB_ANY, VERB_ONLINE_ONLY, +VERB_SCOPE(, verb_cancel, "cancel", "[JOB…]\0", VERB_ANY, VERB_ANY, VERB_ONLINE_ONLY, "Cancel all, one, or more jobs"); VERB_GROUP("Environment Commands"); VERB_SCOPE(, verb_show_environment, "show-environment", NULL, VERB_ANY, 1, VERB_ONLINE_ONLY, "Dump environment"); -VERB_SCOPE(, verb_set_environment, "set-environment", "VARIABLE=VALUE…", 2, VERB_ANY, VERB_ONLINE_ONLY, +VERB_SCOPE(, verb_set_environment, "set-environment", "VARIABLE=VALUE…\0", 2, VERB_ANY, VERB_ONLINE_ONLY, "Set one or more environment variables"); -VERB_SCOPE(, verb_set_environment, "unset-environment", "VARIABLE…", 2, VERB_ANY, VERB_ONLINE_ONLY, +VERB_SCOPE(, verb_set_environment, "unset-environment", "VARIABLE…\0", 2, VERB_ANY, VERB_ONLINE_ONLY, "Unset one or more environment variables"); -VERB_SCOPE(, verb_import_environment, "import-environment", "VARIABLE…", VERB_ANY, VERB_ANY, VERB_ONLINE_ONLY, +VERB_SCOPE(, verb_import_environment, "import-environment", "VARIABLE…\0", VERB_ANY, VERB_ANY, VERB_ONLINE_ONLY, "Import all or some environment variables"); VERB_GROUP("Manager State Commands"); @@ -1041,11 +1041,11 @@ VERB_SCOPE(, verb_daemon_reload, "daemon-reload", NULL, 1, "Reload systemd manager configuration"); VERB_SCOPE(, verb_daemon_reload, "daemon-reexec", NULL, 1, 1, VERB_ONLINE_ONLY, "Reexecute systemd manager"); -VERB_SCOPE(, verb_log_setting, "log-level", "[LEVEL]", VERB_ANY, 2, VERB_ONLINE_ONLY, +VERB_SCOPE(, verb_log_setting, "log-level", "[LEVEL]\0", VERB_ANY, 2, VERB_ONLINE_ONLY, "Get/set logging threshold for manager"); -VERB_SCOPE(, verb_log_setting, "log-target", "[TARGET]", VERB_ANY, 2, VERB_ONLINE_ONLY, +VERB_SCOPE(, verb_log_setting, "log-target", "[TARGET]\0", VERB_ANY, 2, VERB_ONLINE_ONLY, "Get/set logging target for manager"); -VERB_SCOPE(, verb_service_watchdogs, "service-watchdogs", "[BOOL]", VERB_ANY, 2, VERB_ONLINE_ONLY, +VERB_SCOPE(, verb_service_watchdogs, "service-watchdogs", "[BOOL]\0", VERB_ANY, 2, VERB_ONLINE_ONLY, "Get/set service watchdog state"); VERB_GROUP("System Commands"); @@ -1068,9 +1068,9 @@ VERB_SCOPE(, verb_start_system_special, "kexec", NULL, VERB_ANY, "Shut down and reboot the system with kexec"); VERB_SCOPE(, verb_start_system_special, "soft-reboot", NULL, VERB_ANY, 1, VERB_ONLINE_ONLY, "Shut down and reboot userspace"); -VERB_SCOPE(, verb_start_special, "exit", "[EXIT_CODE]", VERB_ANY, 2, VERB_ONLINE_ONLY, +VERB_SCOPE(, verb_start_special, "exit", "[EXIT_CODE]\0", VERB_ANY, 2, VERB_ONLINE_ONLY, "Request user instance or container exit"); -VERB_SCOPE(, verb_switch_root, "switch-root", "[ROOT [INIT]]", VERB_ANY, VERB_ANY, VERB_ONLINE_ONLY, +VERB_SCOPE(, verb_switch_root, "switch-root", "[ROOT [INIT]]\0", VERB_ANY, VERB_ANY, VERB_ONLINE_ONLY, "Change to a different root file system"); VERB_SCOPE(, verb_start_system_special, "sleep", NULL, VERB_ANY, 1, VERB_ONLINE_ONLY, "Put the system to sleep (through one of the operations below)"); diff --git a/src/sysupdate/sysupdate.c b/src/sysupdate/sysupdate.c index b56bf3883f..73ae5cebe8 100644 --- a/src/sysupdate/sysupdate.c +++ b/src/sysupdate/sysupdate.c @@ -1942,7 +1942,7 @@ static int verify_polkit(Context *context, sd_varlink *link, const char *action, &s->polkit_registry); } -VERB(verb_list, "list", "[VERSION]", VERB_ANY, 2, VERB_DEFAULT, +VERB(verb_list, "list", "[VERSION]\0", VERB_ANY, 2, VERB_DEFAULT, "Show installed and available versions"); static int verb_list(int argc, char *argv[], uintptr_t _data, void *userdata) { _cleanup_(context_done) Context context = CONTEXT_NULL; @@ -2019,7 +2019,7 @@ static int verb_list(int argc, char *argv[], uintptr_t _data, void *userdata) { } } -VERB(verb_features, "features", "[FEATURE]", VERB_ANY, 2, 0, +VERB(verb_features, "features", "[FEATURE]\0", VERB_ANY, 2, 0, "Show optional features"); static int verb_features(int argc, char *argv[], uintptr_t _data, void *userdata) { _cleanup_(context_done) Context context = CONTEXT_NULL; @@ -2312,9 +2312,9 @@ static int context_enable_feature( return ret; } -VERB(verb_enable_feature, "enable-feature", "FEATURE…", 1, VERB_ANY, 0, +VERB(verb_enable_feature, "enable-feature", "FEATURE…\0", 1, VERB_ANY, 0, "Enable optional feature"); -VERB(verb_enable_feature, "disable-feature", "FEATURE…", 1, VERB_ANY, 0, +VERB(verb_enable_feature, "disable-feature", "FEATURE…\0", 1, VERB_ANY, 0, "Disable optional feature"); static int verb_enable_feature(int argc, char *argv[], uintptr_t _data, void *userdata) { bool enable = streq(argv[0], "enable-feature"); @@ -2759,7 +2759,7 @@ static int verb_update_impl(int argc, char **argv, UpdateActionFlags action_flag } } -VERB(verb_update, "update", "[VERSION]", VERB_ANY, 2, 0, +VERB(verb_update, "update", "[VERSION]\0", VERB_ANY, 2, 0, "Install new version now"); static int verb_update(int argc, char *argv[], uintptr_t _data, void *userdata) { UpdateActionFlags flags = UPDATE_ACTION_INSTALL; @@ -2770,7 +2770,7 @@ static int verb_update(int argc, char *argv[], uintptr_t _data, void *userdata) return verb_update_impl(argc, argv, flags); } -VERB(verb_acquire, "acquire", "[VERSION]", VERB_ANY, 2, 0, +VERB(verb_acquire, "acquire", "[VERSION]\0", VERB_ANY, 2, 0, "Acquire (download) new version now"); static int verb_acquire(int argc, char *argv[], uintptr_t _data, void *userdata) { return verb_update_impl(argc, argv, UPDATE_ACTION_ACQUIRE); @@ -3163,9 +3163,9 @@ static int vl_method_list_targets(sd_varlink *link, sd_json_variant *parameters, SD_JSON_BUILD_PAIR_VARIANT("targets", l)); } -VERB(verb_enable_component, "enable-component", "COMPONENT…", 1, VERB_ANY, 0, +VERB(verb_enable_component, "enable-component", "COMPONENT…\0", 1, VERB_ANY, 0, "Enable component"); -VERB(verb_enable_component, "disable-component", "COMPONENT…", 1, VERB_ANY, 0, +VERB(verb_enable_component, "disable-component", "COMPONENT…\0", 1, VERB_ANY, 0, "Disable component"); static int verb_enable_component(int argc, char *argv[], uintptr_t _data, void *userdata) { bool enable = streq(argv[0], "enable-component"); diff --git a/src/sysupdate/updatectl.c b/src/sysupdate/updatectl.c index cfe6e031ff..315d0e7381 100644 --- a/src/sysupdate/updatectl.c +++ b/src/sysupdate/updatectl.c @@ -651,7 +651,7 @@ static int describe(sd_bus *bus, const char *target_path, const char *version) { return table_print_with_pager(table, SD_JSON_FORMAT_OFF, arg_pager_flags, arg_legend); } -VERB(verb_list, "list", "[TARGET[@VERSION]]", VERB_ANY, 2, VERB_DEFAULT|VERB_ONLINE_ONLY, +VERB(verb_list, "list", "[TARGET[@VERSION]]\0", VERB_ANY, 2, VERB_DEFAULT|VERB_ONLINE_ONLY, "List available targets and versions"); static int verb_list(int argc, char *argv[], uintptr_t _data, void *userdata) { sd_bus *bus = ASSERT_PTR(userdata); @@ -764,7 +764,7 @@ static int check_finished(sd_bus_message *reply, void *userdata, sd_bus_error *r return 0; } -VERB(verb_check, "check", "[TARGET...]", VERB_ANY, VERB_ANY, VERB_ONLINE_ONLY, +VERB(verb_check, "check", "[TARGET...]\0", VERB_ANY, VERB_ANY, VERB_ONLINE_ONLY, "Check for updates"); static int verb_check(int argc, char *argv[], uintptr_t _data, void *userdata) { sd_bus *bus = ASSERT_PTR(userdata); @@ -1306,7 +1306,7 @@ static int do_update(sd_bus *bus, char **targets) { return did_anything ? 1 : 0; } -VERB(verb_update, "update", "[TARGET[@VERSION]...]", VERB_ANY, VERB_ANY, VERB_ONLINE_ONLY, +VERB(verb_update, "update", "[TARGET[@VERSION]...]\0", VERB_ANY, VERB_ANY, VERB_ONLINE_ONLY, "Install updates"); static int verb_update(int argc, char *argv[], uintptr_t _data, void *userdata) { sd_bus *bus = ASSERT_PTR(userdata); @@ -1361,7 +1361,7 @@ static int do_vacuum(sd_bus *bus, const char *target, const char *path) { return count + disabled > 0 ? 1 : 0; } -VERB(verb_vacuum, "vacuum", "[TARGET...]", VERB_ANY, VERB_ANY, VERB_ONLINE_ONLY, +VERB(verb_vacuum, "vacuum", "[TARGET...]\0", VERB_ANY, VERB_ANY, VERB_ONLINE_ONLY, "Clean up old updates"); static int verb_vacuum(int argc, char *argv[], uintptr_t _data, void *userdata) { sd_bus *bus = ASSERT_PTR(userdata); @@ -1507,7 +1507,7 @@ static int list_features(sd_bus *bus) { return table_print_with_pager(table, SD_JSON_FORMAT_OFF, arg_pager_flags, arg_legend); } -VERB(verb_features, "features", "[FEATURE]", VERB_ANY, 2, VERB_ONLINE_ONLY, +VERB(verb_features, "features", "[FEATURE]\0", VERB_ANY, 2, VERB_ONLINE_ONLY, "List and inspect optional features on host OS"); static int verb_features(int argc, char *argv[], uintptr_t _data, void *userdata) { sd_bus *bus = ASSERT_PTR(userdata); @@ -1558,9 +1558,9 @@ static int verb_features(int argc, char *argv[], uintptr_t _data, void *userdata return table_print_with_pager(table, SD_JSON_FORMAT_OFF, arg_pager_flags, false); } -VERB(verb_enable, "enable", "FEATURE...", 2, VERB_ANY, VERB_ONLINE_ONLY, +VERB(verb_enable, "enable", "FEATURE...\0", 2, VERB_ANY, VERB_ONLINE_ONLY, "Enable optional feature on host OS"); -VERB(verb_enable, "disable", "FEATURE...", 2, VERB_ANY, VERB_ONLINE_ONLY, +VERB(verb_enable, "disable", "FEATURE...\0", 2, VERB_ANY, VERB_ONLINE_ONLY, "Disable optional feature on host OS"); static int verb_enable(int argc, char *argv[], uintptr_t _data, void *userdata) { sd_bus *bus = ASSERT_PTR(userdata); diff --git a/src/test/test-verbs.c b/src/test/test-verbs.c index 8fc9e2a25c..22beda3541 100644 --- a/src/test/test-verbs.c +++ b/src/test/test-verbs.c @@ -96,7 +96,7 @@ static const CommandDescription introspect_command = { static const Verb introspect_verbs[] = { { .flags = VERB_COMMAND_MARKER, .data = (uintptr_t) &introspect_command }, { .verb = "alpha", .min_args = VERB_ANY, .max_args = VERB_ANY, .dispatch = noop_dispatcher, - .argspec = "ARG", .help = "Alpha verb", .option_namespace = "test-verbs-alpha" }, + .argspec = "FORM1\0FORM2…\0", .help = "Alpha verb", .option_namespace = "test-verbs-alpha" }, { .verb = "beta", .min_args = VERB_ANY, .max_args = VERB_ANY, .dispatch = noop_dispatcher, .help = "Beta verb" }, {} diff --git a/src/timedate/timedatectl.c b/src/timedate/timedatectl.c index 8ff135b9a7..c388768dbb 100644 --- a/src/timedate/timedatectl.c +++ b/src/timedate/timedatectl.c @@ -237,7 +237,7 @@ static int verb_show(int argc, char *argv[], uintptr_t _data, void *userdata) { return 0; } -VERB(verb_set_time, "set-time", "TIME", 2, 2, 0, "Set system time"); +VERB(verb_set_time, "set-time", "TIME\0", 2, 2, 0, "Set system time"); static int verb_set_time(int argc, char *argv[], uintptr_t _data, void *userdata) { _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL; sd_bus *bus = userdata; @@ -263,7 +263,7 @@ static int verb_set_time(int argc, char *argv[], uintptr_t _data, void *userdata return 0; } -VERB(verb_set_timezone, "set-timezone", "ZONE", 2, 2, 0, "Set system time zone"); +VERB(verb_set_timezone, "set-timezone", "ZONE\0", 2, 2, 0, "Set system time zone"); static int verb_set_timezone(int argc, char *argv[], uintptr_t _data, void *userdata) { _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL; sd_bus *bus = userdata; @@ -301,7 +301,7 @@ static int verb_list_timezones(int argc, char *argv[], uintptr_t _data, void *us return 0; } -VERB(verb_set_local_rtc, "set-local-rtc", "BOOL", 2, 2, 0, "Control whether RTC is in local time"); +VERB(verb_set_local_rtc, "set-local-rtc", "BOOL\0", 2, 2, 0, "Control whether RTC is in local time"); static int verb_set_local_rtc(int argc, char *argv[], uintptr_t _data, void *userdata) { _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL; sd_bus *bus = userdata; @@ -333,7 +333,7 @@ static int verb_set_local_rtc(int argc, char *argv[], uintptr_t _data, void *use return 0; } -VERB(verb_set_ntp, "set-ntp", "BOOL", 2, 2, 0, "Enable or disable network time synchronization"); +VERB(verb_set_ntp, "set-ntp", "BOOL\0", 2, 2, 0, "Enable or disable network time synchronization"); static int verb_set_ntp(int argc, char *argv[], uintptr_t _data, void *userdata) { _cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL; _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL; @@ -847,7 +847,7 @@ static int parse_ifindex_bus(sd_bus *bus, const char *str) { return i; } -VERB(verb_ntp_servers, "ntp-servers", "INTERFACE SERVER…", 3, VERB_ANY, 0, +VERB(verb_ntp_servers, "ntp-servers", "INTERFACE SERVER…\0", 3, VERB_ANY, 0, "Set the interface specific NTP servers"); static int verb_ntp_servers(int argc, char *argv[], uintptr_t _data, void *userdata) { _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL; @@ -880,7 +880,7 @@ static int verb_ntp_servers(int argc, char *argv[], uintptr_t _data, void *userd return 0; } -VERB(verb_revert, "revert", "INTERFACE", 2, 2, 0, "Revert the interface specific NTP servers"); +VERB(verb_revert, "revert", "INTERFACE\0", 2, 2, 0, "Revert the interface specific NTP servers"); static int verb_revert(int argc, char *argv[], uintptr_t _data, void *userdata) { _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL; sd_bus *bus = ASSERT_PTR(userdata); diff --git a/src/udev/iocost/iocost.c b/src/udev/iocost/iocost.c index 951243fee9..3501f192ba 100644 --- a/src/udev/iocost/iocost.c +++ b/src/udev/iocost/iocost.c @@ -261,13 +261,13 @@ static int query_solutions_for_path(const char *path) { return 0; } -VERB(verb_query, "query", "PATH", 2, 2, 0, +VERB(verb_query, "query", "PATH\0", 2, 2, 0, "Query the known solution for the device"); static int verb_query(int argc, char *argv[], uintptr_t _data, void *userdata) { return query_solutions_for_path(ASSERT_PTR(argv[1])); } -VERB(verb_apply, "apply", "PATH [SOLUTION]", 2, 3, 0, +VERB(verb_apply, "apply", "PATH [SOLUTION]\0", 2, 3, 0, "Apply solution for the device if found, do nothing otherwise"); static int verb_apply(int argc, char *argv[], uintptr_t _data, void *userdata) { return apply_solution_for_path( diff --git a/src/udev/udevadm.c b/src/udev/udevadm.c index ebbbadcea0..18d554ab1a 100644 --- a/src/udev/udevadm.c +++ b/src/udev/udevadm.c @@ -47,17 +47,17 @@ static int help(void) { VERB_COMMON_HELP(help); -VERB_SCOPE(, verb_info_main, "info", "[DEVPATH|FILE]", VERB_ANY, VERB_ANY, 0, "Query sysfs or the udev database"); -VERB_SCOPE(, verb_trigger_main, "trigger", "DEVPATH", VERB_ANY, VERB_ANY, 0, "Request events from the kernel"); +VERB_SCOPE(, verb_info_main, "info", "[DEVPATH|FILE]\0", VERB_ANY, VERB_ANY, 0, "Query sysfs or the udev database"); +VERB_SCOPE(, verb_trigger_main, "trigger", "DEVPATH\0", VERB_ANY, VERB_ANY, 0, "Request events from the kernel"); VERB_SCOPE(, verb_settle_main, "settle", NULL, VERB_ANY, VERB_ANY, 0, "Wait for pending udev events"); -VERB_SCOPE(, verb_control_main, "control", "OPTION", VERB_ANY, VERB_ANY, 0, "Control the udev daemon"); +VERB_SCOPE(, verb_control_main, "control", "OPTION\0", VERB_ANY, VERB_ANY, 0, "Control the udev daemon"); VERB_SCOPE(, verb_monitor_main, "monitor", NULL, VERB_ANY, VERB_ANY, 0, "Listen to kernel and udev events"); -VERB_SCOPE(, verb_test_main, "test", "DEVPATH", VERB_ANY, VERB_ANY, 0, "Test an event run"); -VERB_SCOPE(, verb_builtin_main, "test-builtin", "COMMAND DEVPATH", VERB_ANY, VERB_ANY, 0, "Test a built-in command"); -VERB_SCOPE(, verb_verify_main, "verify", "[FILE…]", VERB_ANY, VERB_ANY, 0, "Verify udev rules files"); -VERB_SCOPE(, verb_cat_main, "cat", "[FILE…]", VERB_ANY, VERB_ANY, 0, "Show udev rules files"); -VERB_SCOPE(, verb_wait_main, "wait", "DEVICE [DEVICE…]", VERB_ANY, VERB_ANY, 0, "Wait for device or device symlink"); -VERB_SCOPE(, verb_lock_main, "lock", "[OPTIONS…] COMMAND", VERB_ANY, VERB_ANY, 0, "Lock a block device"); +VERB_SCOPE(, verb_test_main, "test", "DEVPATH\0", VERB_ANY, VERB_ANY, 0, "Test an event run"); +VERB_SCOPE(, verb_builtin_main, "test-builtin", "COMMAND DEVPATH\0", VERB_ANY, VERB_ANY, 0, "Test a built-in command"); +VERB_SCOPE(, verb_verify_main, "verify", "[FILE…]\0", VERB_ANY, VERB_ANY, 0, "Verify udev rules files"); +VERB_SCOPE(, verb_cat_main, "cat", "[FILE…]\0", VERB_ANY, VERB_ANY, 0, "Show udev rules files"); +VERB_SCOPE(, verb_wait_main, "wait", "DEVICE [DEVICE…]\0", VERB_ANY, VERB_ANY, 0, "Wait for device or device symlink"); +VERB_SCOPE(, verb_lock_main, "lock", "[OPTIONS…] COMMAND\0", VERB_ANY, VERB_ANY, 0, "Lock a block device"); VERB_SCOPE(, verb_hwdb_main, "hwdb", NULL, VERB_ANY, VERB_ANY, 0, /* help= */ NULL); /* deprecated */ VERB_NOARG(verb_version_main, "version", /* help= */ NULL); diff --git a/src/userdb/userdbctl.c b/src/userdb/userdbctl.c index 559a682deb..ef05967a90 100644 --- a/src/userdb/userdbctl.c +++ b/src/userdb/userdbctl.c @@ -418,7 +418,7 @@ static int table_add_uid_map( return n_added; } -VERB(verb_display_user, "user", "[USER…]", VERB_ANY, VERB_ANY, VERB_DEFAULT, +VERB(verb_display_user, "user", "[USER…]\0", VERB_ANY, VERB_ANY, VERB_DEFAULT, "Inspect user"); static int verb_display_user(int argc, char *argv[], uintptr_t _data, void *userdata) { _cleanup_(table_unrefp) Table *table = NULL; @@ -763,7 +763,7 @@ static int add_unavailable_gid(Table *table, uid_t start, uid_t end) { return 2; } -VERB(verb_display_group, "group", "[GROUP…]", VERB_ANY, VERB_ANY, 0, +VERB(verb_display_group, "group", "[GROUP…]\0", VERB_ANY, VERB_ANY, 0, "Inspect group"); static int verb_display_group(int argc, char *argv[], uintptr_t _data, void *userdata) { _cleanup_(table_unrefp) Table *table = NULL; @@ -966,9 +966,9 @@ static int show_membership(const char *user, const char *group, Table *table) { return 0; } -VERB(verb_display_memberships, "users-in-group", "[GROUP…]", VERB_ANY, VERB_ANY, 0, +VERB(verb_display_memberships, "users-in-group", "[GROUP…]\0", VERB_ANY, VERB_ANY, 0, "Show users that are members of specified groups"); -VERB(verb_display_memberships, "groups-of-user", "[USER…]", VERB_ANY, VERB_ANY, 0, +VERB(verb_display_memberships, "groups-of-user", "[USER…]\0", VERB_ANY, VERB_ANY, 0, "Show groups the specified users are members of"); static int verb_display_memberships(int argc, char *argv[], uintptr_t _data, void *userdata) { _cleanup_(table_unrefp) Table *table = NULL; @@ -1131,7 +1131,7 @@ static int verb_display_services(int argc, char *argv[], uintptr_t _data, void * return 0; } -VERB(verb_ssh_authorized_keys, "ssh-authorized-keys", "USER", 2, VERB_ANY, 0, +VERB(verb_ssh_authorized_keys, "ssh-authorized-keys", "USER\0", 2, VERB_ANY, 0, "Show SSH authorized keys for user"); static int verb_ssh_authorized_keys(int argc, char *argv[], uintptr_t _data, void *userdata) { _cleanup_(user_record_unrefp) UserRecord *ur = NULL; diff --git a/src/varlinkctl/varlinkctl.c b/src/varlinkctl/varlinkctl.c index 34f47d44ad..8a1a2655b1 100644 --- a/src/varlinkctl/varlinkctl.c +++ b/src/varlinkctl/varlinkctl.c @@ -295,8 +295,8 @@ COMMAND( .pager_flags = &arg_pager_flags, ); -VERB(verb_info, "info", "ADDRESS", 2, 2, 0, "Show service information"); -VERB(verb_info, "list-interfaces", "ADDRESS", 2, 2, 0, "List interfaces implemented by service"); +VERB(verb_info, "info", "ADDRESS\0", 2, 2, 0, "Show service information"); +VERB(verb_info, "list-interfaces", "ADDRESS\0", 2, 2, 0, "List interfaces implemented by service"); static int verb_info(int argc, char *argv[], uintptr_t _data, void *userdata) { _cleanup_(sd_varlink_unrefp) sd_varlink *vl = NULL; const char *url; @@ -390,8 +390,8 @@ typedef struct GetInterfaceDescriptionData { const char *description; } GetInterfaceDescriptionData; -VERB(verb_introspect, "introspect", "ADDRESS [INTERFACE…]", 2, VERB_ANY, 0, "Show interface definition"); -VERB(verb_introspect, "list-methods", "ADDRESS [INTERFACE…]", 2, VERB_ANY, 0, +VERB(verb_introspect, "introspect", "ADDRESS [INTERFACE…]\0", 2, VERB_ANY, 0, "Show interface definition"); +VERB(verb_introspect, "list-methods", "ADDRESS [INTERFACE…]\0", 2, VERB_ANY, 0, "List methods implemented by services or specific interfaces"); static int verb_introspect(int argc, char *argv[], uintptr_t _data, void *userdata) { _cleanup_(sd_varlink_unrefp) sd_varlink *vl = NULL; @@ -709,7 +709,7 @@ static int varlink_call_and_upgrade(const char *url, const char *method, sd_json return 0; } -VERB(verb_call, "call", "ADDRESS METHOD [PARAMS]", 3, VERB_ANY, 0, "Invoke method"); +VERB(verb_call, "call", "ADDRESS METHOD [PARAMS]\0", 3, VERB_ANY, 0, "Invoke method"); static int verb_call(int argc, char *argv[], uintptr_t _data, void *userdata) { _cleanup_(sd_json_variant_unrefp) sd_json_variant *jp = NULL; _cleanup_(sd_varlink_unrefp) sd_varlink *vl = NULL; @@ -983,7 +983,7 @@ static int verb_call(int argc, char *argv[], uintptr_t _data, void *userdata) { return 0; } -VERB(verb_validate_idl, "validate-idl", "[FILE]", 1, 2, 0, "Validate interface description"); +VERB(verb_validate_idl, "validate-idl", "[FILE]\0", 1, 2, 0, "Validate interface description"); static int verb_validate_idl(int argc, char *argv[], uintptr_t _data, void *userdata) { _cleanup_(sd_varlink_interface_freep) sd_varlink_interface *vi = NULL; _cleanup_free_ char *text = NULL; @@ -1386,7 +1386,7 @@ static int method_serve_upgrade(sd_varlink *link, sd_json_variant *parameters, s return 0; } -VERB(verb_serve, "serve", "METHOD CMDLINE…", 3, VERB_ANY, 0, "Serve a command via varlink protocol upgrade"); +VERB(verb_serve, "serve", "METHOD CMDLINE…\0", 3, VERB_ANY, 0, "Serve a command via varlink protocol upgrade"); static int verb_serve(int argc, char *argv[], uintptr_t _data, void *userdata) { _cleanup_(sd_varlink_server_unrefp) sd_varlink_server *s = NULL; _cleanup_(sd_event_unrefp) sd_event *event = NULL; diff --git a/src/veritysetup/veritysetup.c b/src/veritysetup/veritysetup.c index 6e079ac746..cd640bac22 100644 --- a/src/veritysetup/veritysetup.c +++ b/src/veritysetup/veritysetup.c @@ -303,7 +303,7 @@ COMMAND( .man_pages = "systemd-veritysetup@.service(8)\0", ); -VERB(verb_attach, "attach", "VOLUME DATADEVICE HASHDEVICE ROOTHASH [OPTIONS]", 5, 6, 0, +VERB(verb_attach, "attach", "VOLUME DATADEVICE HASHDEVICE ROOTHASH [OPTIONS]\0", 5, 6, 0, "Attach a verity-protected block device"); static int verb_attach(int argc, char *argv[], uintptr_t _data, void *userdata) { _cleanup_(crypt_freep) struct crypt_device *cd = NULL; @@ -441,7 +441,7 @@ static int verb_attach(int argc, char *argv[], uintptr_t _data, void *userdata) return 0; } -VERB(verb_detach, "detach", "VOLUME", 2, 2, 0, +VERB(verb_detach, "detach", "VOLUME\0", 2, 2, 0, "Detach a verity-protected block device"); static int verb_detach(int argc, char *argv[], uintptr_t _data, void *userdata) { _cleanup_(crypt_freep) struct crypt_device *cd = NULL; From 3cfe4563235a0f420abe5ee49c577daabb36e010 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Zbigniew=20J=C4=99drzejewski-Szmek?= Date: Wed, 26 Aug 2026 10:09:19 +0200 Subject: [PATCH 04/21] shared/verbs: add command_print_verb_help() A helper to print the help of a single verb, derived entirely from the data in the SYSTEMD_VERBS and SYSTEMD_OPTIONS sections: the synopsis is built from the verb name and argspec, the one-line verb help serves as the abstract, the options come from the verb's option namespace, and the man page links from the command the verb belongs to. A flag allows suppressing the man page links for callers which append custom sections. --- src/shared/verbs.c | 66 +++++++++++++++++++++++++++++++++++++++++++ src/shared/verbs.h | 18 ++++++++++++ src/test/test-verbs.c | 9 ++++++ 3 files changed, 93 insertions(+) diff --git a/src/shared/verbs.c b/src/shared/verbs.c index ddc11cc004..d65999dd70 100644 --- a/src/shared/verbs.c +++ b/src/shared/verbs.c @@ -564,6 +564,72 @@ int _command_print_help_full( return 0; } +int _command_print_verb_help( + const Verb verbs[], + const Verb verbs_end[], + const Option options[], + const Option options_end[], + const char *name) { + + int r; + + assert(name); + + const Verb *verb = ASSERT_PTR(_verbs_find_verb(verbs, verbs_end, name)); + + /* The owning command is described by the closest preceding VERB_COMMAND_MARKER entry. */ + const Verb *cmdverb = verb; + while (!FLAGS_SET(cmdverb->flags, VERB_COMMAND_MARKER)) { + assert(cmdverb > verbs); + cmdverb--; + } + const CommandDescription *cmd = (const CommandDescription*) ASSERT_PTR(cmdverb->data); + + if (cmd->pager_flags) + pager_open(*cmd->pager_flags); + + /* If argspec is empty, print one line with no argspec. Use " " as placeholder for the empty spec. */ + NULSTR_FOREACH(spec, verb->argspec ?: " \0") { + bool some = !streq(spec, " "); + _cleanup_free_ char *line = strjoin( + "[OPTION…] ", verb->verb, + verb->option_namespace ? " [OPTION…]" : "", + some ? " " : NULL, + some ? spec : NULL); + if (!line) + return log_oom(); + help_cmdline(line); + }; + + if (verb->help) { + const char *s = strjoina(ansi_highlight(), ansi_add_italics()), + *t = strjoina(verb->help, "."); + + r = print_wrapped(t, s); + if (r < 0) + return r; + } + + if (verb->option_namespace) { + _cleanup_(table_unrefp) Table *table = NULL; + r = _option_parser_get_help_table_full(options, options_end, verb->option_namespace, + /* group= */ NULL, &table); + if (r < 0) + return r; + + help_section("Options"); + r = table_print_or_warn(table); + if (r < 0) + return r; + } + + r = print_man_links(cmd->man_pages); + if (r < 0) + return log_error_errno(r, "Failed to print man page links: %m"); + + return 0; +} + static int verb_build_json( const Verb *verb, const Option options[], diff --git a/src/shared/verbs.h b/src/shared/verbs.h index cda8db56d3..0288548a8c 100644 --- a/src/shared/verbs.h +++ b/src/shared/verbs.h @@ -183,6 +183,24 @@ static inline int verb_help_auto(int argc, char **argv, uintptr_t data, void *us return command_print_help_name((const char*) data); } +/* Print the help for a single verb: the synopsis, the one-line help as abstract, the options from + * the verb's option namespace (see VERB_SCOPE_NS*), and the man page links of the command the verb + * belongs to. The verb is attributed to the closest preceding COMMAND() in the verbs section, so: + * name must refer to an existing verb owned by such a command (asserted otherwise), verb names + * must be unique across the whole binary, commands with COMMAND_VERBS_SHARED are not + * disambiguated, and a declared option namespace must contain at least one non-hidden option. */ +int _command_print_verb_help( + const Verb verbs[], + const Verb verbs_end[], + const Option options[], + const Option options_end[], + const char *name); +#define command_print_verb_help(name) \ + _command_print_verb_help( \ + __start_SYSTEMD_VERBS, __stop_SYSTEMD_VERBS, \ + __start_SYSTEMD_OPTIONS, __stop_SYSTEMD_OPTIONS, \ + name) + /* Print a machine-readable description of the program's commands, verbs, and options in the format * defined by the CLI-Introspection Specification. One command object is emitted for each * VERB_COMMAND() in the verb table, so multicall binaries are described in full. */ diff --git a/src/test/test-verbs.c b/src/test/test-verbs.c index 22beda3541..c255c5840c 100644 --- a/src/test/test-verbs.c +++ b/src/test/test-verbs.c @@ -114,4 +114,13 @@ TEST(introspect_cli_verb_options) { SD_JSON_FORMAT_OFF)); } +TEST(command_print_verb_help) { + ASSERT_OK(_command_print_verb_help(introspect_verbs, introspect_verbs + ELEMENTSOF(introspect_verbs) - 1, + introspect_options, introspect_options + ELEMENTSOF(introspect_options) - 1, + "alpha")); + ASSERT_OK(_command_print_verb_help(introspect_verbs, introspect_verbs + ELEMENTSOF(introspect_verbs) - 1, + introspect_options, introspect_options + ELEMENTSOF(introspect_options) - 1, + "beta")); +} + DEFINE_TEST_MAIN(LOG_INFO); From 84d19dc8f9f4446baf10adae8061e29f9ac61b5d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Zbigniew=20J=C4=99drzejewski-Szmek?= Date: Wed, 26 Aug 2026 14:08:25 +0200 Subject: [PATCH 05/21] shared/verbs: add flag VERB_OPTION_REQUIRED --- src/shared/verbs.c | 12 +++++++++--- src/shared/verbs.h | 9 +++++---- src/test/test-verbs.c | 6 ++++++ 3 files changed, 20 insertions(+), 7 deletions(-) diff --git a/src/shared/verbs.c b/src/shared/verbs.c index d65999dd70..8515dbecf7 100644 --- a/src/shared/verbs.c +++ b/src/shared/verbs.c @@ -175,10 +175,11 @@ static int verb_add_help_one(Table *table, const Verb *verb) { assert(table); assert(verb); - bool is_default = FLAGS_SET(verb->flags, VERB_DEFAULT); _cleanup_free_ char *cell = NULL; int r; + bool is_default = FLAGS_SET(verb->flags, VERB_DEFAULT); + /* The synopsis cell contains one line per entry in the argspec nulstr. An unset argspec is * equivalent to a single empty specification. */ for (const char *spec = empty_to_null(verb->argspec);;) { @@ -190,7 +191,8 @@ static int verb_add_help_one(Table *table, const Verb *verb) { verb->verb, spec ? " " : "", strempty(spec), - is_default ? "]" : ""); + is_default ? "]" : "", + FLAGS_SET(verb->flags, VERB_OPTION_REQUIRED) ? " OPTION" : ""); if (!s) return log_oom(); @@ -588,12 +590,16 @@ int _command_print_verb_help( if (cmd->pager_flags) pager_open(*cmd->pager_flags); + const char *optionspec = + FLAGS_SET(verb->flags, VERB_OPTION_REQUIRED) ? + " OPTION…" : verb->option_namespace ? " [OPTION…]" : ""; + /* If argspec is empty, print one line with no argspec. Use " " as placeholder for the empty spec. */ NULSTR_FOREACH(spec, verb->argspec ?: " \0") { bool some = !streq(spec, " "); _cleanup_free_ char *line = strjoin( "[OPTION…] ", verb->verb, - verb->option_namespace ? " [OPTION…]" : "", + optionspec, some ? " " : NULL, some ? spec : NULL); if (!line) diff --git a/src/shared/verbs.h b/src/shared/verbs.h index 0288548a8c..cd679f404e 100644 --- a/src/shared/verbs.h +++ b/src/shared/verbs.h @@ -28,10 +28,11 @@ typedef struct CommandDescription { } CommandDescription; typedef enum VerbFlags { - VERB_DEFAULT = 1 << 0, /* The verb to run if no verb is specified */ - VERB_ONLINE_ONLY = 1 << 1, /* Just do nothing when running in chroot or offline */ - VERB_COMMAND_MARKER = 1 << 2, /* Header entry with command description */ - VERB_GROUP_MARKER = 1 << 3, /* Fake verb entry to separate groups */ + VERB_DEFAULT = 1 << 0, /* The verb to run if no verb is specified */ + VERB_ONLINE_ONLY = 1 << 1, /* Just do nothing when running in chroot or offline */ + VERB_OPTION_REQUIRED = 1 << 2, /* The verb always requires an option to be present */ + VERB_COMMAND_MARKER = 1 << 3, /* Header entry with command description */ + VERB_GROUP_MARKER = 1 << 4, /* Fake verb entry to separate groups */ } VerbFlags; /* Note: see the comment on struct Option in options.h for why _alignptr_ is required here. */ diff --git a/src/test/test-verbs.c b/src/test/test-verbs.c index c255c5840c..42670c9593 100644 --- a/src/test/test-verbs.c +++ b/src/test/test-verbs.c @@ -99,6 +99,9 @@ static const Verb introspect_verbs[] = { .argspec = "FORM1\0FORM2…\0", .help = "Alpha verb", .option_namespace = "test-verbs-alpha" }, { .verb = "beta", .min_args = VERB_ANY, .max_args = VERB_ANY, .dispatch = noop_dispatcher, .help = "Beta verb" }, + { .verb = "gamma", .flags = VERB_OPTION_REQUIRED, + .min_args = VERB_ANY, .max_args = VERB_ANY, .dispatch = noop_dispatcher, + .help = "Gamma verb" }, {} }; @@ -121,6 +124,9 @@ TEST(command_print_verb_help) { ASSERT_OK(_command_print_verb_help(introspect_verbs, introspect_verbs + ELEMENTSOF(introspect_verbs) - 1, introspect_options, introspect_options + ELEMENTSOF(introspect_options) - 1, "beta")); + ASSERT_OK(_command_print_verb_help(introspect_verbs, introspect_verbs + ELEMENTSOF(introspect_verbs) - 1, + introspect_options, introspect_options + ELEMENTSOF(introspect_options) - 1, + "gamma")); } DEFINE_TEST_MAIN(LOG_INFO); From 4aea8cedd2c8ede0313ad99fd7d9dd64dd0e1de0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Zbigniew=20J=C4=99drzejewski-Szmek?= Date: Wed, 26 Aug 2026 14:52:36 +0200 Subject: [PATCH 06/21] shared/verbs: add a footer for verbs This will be needed for 'udevadm hwdb' which carries a footer. --- src/shared/verbs.c | 7 +++++++ src/shared/verbs.h | 34 +++++++++++++++++++--------------- src/test/test-verbs.c | 2 +- 3 files changed, 27 insertions(+), 16 deletions(-) diff --git a/src/shared/verbs.c b/src/shared/verbs.c index 8515dbecf7..7f49a8ee0b 100644 --- a/src/shared/verbs.c +++ b/src/shared/verbs.c @@ -629,6 +629,10 @@ int _command_print_verb_help( return r; } + r = print_wrapped(verb->footer, /* footer_ansi_seq= */ NULL); + if (r < 0) + return r; + r = print_man_links(cmd->man_pages); if (r < 0) return log_error_errno(r, "Failed to print man page links: %m"); @@ -672,6 +676,9 @@ static int verb_build_json( !!verb->help, "abstract", SD_JSON_BUILD_STRV(STRV_MAKE(verb->help))), SD_JSON_BUILD_PAIR_CONDITION(!!opts, "options", SD_JSON_BUILD_VARIANT(opts)), + SD_JSON_BUILD_PAIR_CONDITION( + !!verb->footer, + "postscript", SD_JSON_BUILD_STRV(STRV_MAKE(verb->footer))), SD_JSON_BUILD_PAIR_CONDITION( verb->min_args != VERB_ANY, "minArguments", SD_JSON_BUILD_UNSIGNED(verb->min_args)), diff --git a/src/shared/verbs.h b/src/shared/verbs.h index cd679f404e..e2bed04d54 100644 --- a/src/shared/verbs.h +++ b/src/shared/verbs.h @@ -44,11 +44,12 @@ typedef struct _alignptr_ Verb { uintptr_t data; const char *argspec; /* optional nulstr with specifications of positional args */ const char *help; + const char *footer; /* optional footer to print in --help */ const char *option_namespace; /* optional namespace with the verb's own options */ } Verb; assert_cc(sizeof(Verb) % sizeof(void*) == 0); -#define _VERB_DATA(d, v, ns, a, amin, amax, f, dat, h) \ +#define _VERB_DATA(d, v, ns, a, amin, amax, f, dat, h, ft) \ _section_("SYSTEMD_VERBS") \ _alignptr_ \ _used_ \ @@ -64,31 +65,32 @@ assert_cc(sizeof(Verb) % sizeof(void*) == 0); .data = dat, \ .argspec = a, \ .help = h, \ + .footer = ft, \ .option_namespace = ns, \ } /* Forward-define function d. scope specifies the scope, e.g. static. ns declares the option * namespace with the verb's own options, which are then shown in the verb's help (see * command_print_verb_help()) and reported in the CLI introspection. */ -#define VERB_SCOPE_NS_FULL(scope, d, v, ns, a, amin, amax, f, dat, h) \ +#define VERB_SCOPE_NS_FULL(scope, d, v, ns, a, amin, amax, f, dat, h, ft) \ DISABLE_WARNING_REDUNDANT_DECLS \ scope int d(int, char**, uintptr_t, void*); \ REENABLE_WARNING \ - _VERB_DATA(d, v, ns, a, amin, amax, f, dat, h) + _VERB_DATA(d, v, ns, a, amin, amax, f, dat, h, ft) /* The same as VERB_SCOPE_NS_FULL, but for verbs without options of their own. */ -#define VERB_SCOPE_FULL(scope, d, v, a, amin, amax, f, dat, h) \ - VERB_SCOPE_NS_FULL(scope, d, v, /* ns= */ NULL, a, amin, amax, f, dat, h) +#define VERB_SCOPE_FULL(scope, d, v, a, amin, amax, f, dat, h, ft) \ + VERB_SCOPE_NS_FULL(scope, d, v, /* ns= */ NULL, a, amin, amax, f, dat, h, ft) /* The same as VERB_SCOPE_NS_FULL/VERB_SCOPE_FULL with scope hardwired to 'static'. */ -#define VERB_NS_FULL(d, v, ns, a, amin, amax, f, dat, h) \ - VERB_SCOPE_NS_FULL(static, d, v, ns, a, amin, amax, f, dat, h) +#define VERB_NS_FULL(d, v, ns, a, amin, amax, f, dat, h, ft) \ + VERB_SCOPE_NS_FULL(static, d, v, ns, a, amin, amax, f, dat, h, ft) #define VERB_FULL(d, v, a, amin, amax, f, dat, h) \ - VERB_SCOPE_FULL(static, d, v, a, amin, amax, f, dat, h) + VERB_SCOPE_FULL(static, d, v, a, amin, amax, f, dat, h, /* ft= */ NULL) /* The same as the macros above, but without the data argument. */ #define VERB_SCOPE_NS(scope, d, v, ns, a, amin, amax, f, h) \ - VERB_SCOPE_NS_FULL(scope, d, v, ns, a, amin, amax, f, /* dat= */ 0, h) + VERB_SCOPE_NS_FULL(scope, d, v, ns, a, amin, amax, f, /* dat= */ 0, h, /* ft= */ NULL) #define VERB_SCOPE(scope, d, v, a, amin, amax, f, h) \ - VERB_SCOPE_FULL(scope, d, v, a, amin, amax, f, /* dat= */ 0, h) + VERB_SCOPE_FULL(scope, d, v, a, amin, amax, f, /* dat= */ 0, h, /* ft= */ NULL) #define VERB_NS(d, v, ns, a, amin, amax, f, h) \ VERB_SCOPE_NS(static, d, v, ns, a, amin, amax, f, h) #define VERB(d, v, a, amin, amax, f, h) \ @@ -106,13 +108,15 @@ assert_cc(sizeof(Verb) % sizeof(void*) == 0); * The macro works as a separator between groups and must be between other VERB* stanzas. */ #define VERB_GROUP(gr) \ _VERB_DATA(/* d= */ NULL, /* v= */ gr, /* ns= */ NULL, /* a= */ NULL, \ - /* amin= */ 0, /* amax= */ 0, /* f= */ VERB_GROUP_MARKER, /* dat= */ 0, /* h= */ NULL) + /* amin= */ 0, /* amax= */ 0, /* f= */ VERB_GROUP_MARKER, /* dat= */ 0, \ + /* h= */ NULL, /* ft= */ NULL) -#define _COMMAND(u, ...) \ +#define _COMMAND(u, ...) \ static const CommandDescription UNIQ_T(description, u) = { __VA_ARGS__ }; \ - _VERB_DATA(/* d= */ NULL, /* v= */ NULL, /* ns= */ NULL, /* a= */ NULL, \ - /* amin= */ 0, /* amax= */ 0, /* f= */ VERB_COMMAND_MARKER, \ - /* dat= */ (uintptr_t) &UNIQ_T(description, u), /* h= */ NULL) + _VERB_DATA(/* d= */ NULL, /* v= */ NULL, /* ns= */ NULL, /* a= */ NULL, \ + /* amin= */ 0, /* amax= */ 0, /* f= */ VERB_COMMAND_MARKER, \ + /* dat= */ (uintptr_t) &UNIQ_T(description, u), \ + /* h= */ NULL, /* ft= */ NULL) #define COMMAND(...) _COMMAND(UNIQ, __VA_ARGS__) /* This is magically mapped to the beginning and end of the section */ diff --git a/src/test/test-verbs.c b/src/test/test-verbs.c index 42670c9593..9016a6268c 100644 --- a/src/test/test-verbs.c +++ b/src/test/test-verbs.c @@ -101,7 +101,7 @@ static const Verb introspect_verbs[] = { .help = "Beta verb" }, { .verb = "gamma", .flags = VERB_OPTION_REQUIRED, .min_args = VERB_ANY, .max_args = VERB_ANY, .dispatch = noop_dispatcher, - .help = "Gamma verb" }, + .help = "Gamma verb", .footer = "Gamma footer" }, {} }; From 868d22628c69aff70f69338aa9bc51f0336b94df Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Zbigniew=20J=C4=99drzejewski-Szmek?= Date: Wed, 26 Aug 2026 14:34:50 +0200 Subject: [PATCH 07/21] shared/verbs: add flag VERB_DEPRECATED Previously, verbs were skipped in --help automatically when their .help field was nil. But this doesn't work in two ways: - in introspection data we want to show the option, but marked as deprecated - in udevadm (and maybe other programs with verbs with own options in the future), we need to show help for the verb. So add an explicit flag to mark verbs as deprecated and skip them in help even if .help is set. If there's a footer, print it highlighted in red automatically. --- src/shared/verbs.c | 10 +++++++--- src/shared/verbs.h | 1 + src/test/test-verbs.c | 2 +- 3 files changed, 9 insertions(+), 4 deletions(-) diff --git a/src/shared/verbs.c b/src/shared/verbs.c index 7f49a8ee0b..920b4e0458 100644 --- a/src/shared/verbs.c +++ b/src/shared/verbs.c @@ -312,8 +312,8 @@ int _verbs_get_help_table( if (group_marker) break; /* End of group */ - if (!verb->help) - /* No help string — we do not show the verb */ + if (FLAGS_SET(verb->flags, VERB_DEPRECATED) || !verb->help) + /* Marked as deprecated or no help string — we do not show the verb */ continue; r = verb_add_help_one(table, verb); @@ -629,7 +629,8 @@ int _command_print_verb_help( return r; } - r = print_wrapped(verb->footer, /* footer_ansi_seq= */ NULL); + r = print_wrapped(verb->footer, + FLAGS_SET(verb->flags, VERB_DEPRECATED) ? ansi_highlight_red() : NULL); if (r < 0) return r; @@ -688,6 +689,9 @@ static int verb_build_json( SD_JSON_BUILD_PAIR_CONDITION( FLAGS_SET(verb->flags, VERB_DEFAULT), "isDefault", SD_JSON_BUILD_BOOLEAN(true)), + SD_JSON_BUILD_PAIR_CONDITION( + FLAGS_SET(verb->flags, VERB_DEPRECATED), + "isDeprecated", SD_JSON_BUILD_BOOLEAN(true)), SD_JSON_BUILD_PAIR_CONDITION( FLAGS_SET(verb->flags, VERB_ONLINE_ONLY), "isOnlineOnly", SD_JSON_BUILD_BOOLEAN(true))); diff --git a/src/shared/verbs.h b/src/shared/verbs.h index e2bed04d54..3453c5e72a 100644 --- a/src/shared/verbs.h +++ b/src/shared/verbs.h @@ -33,6 +33,7 @@ typedef enum VerbFlags { VERB_OPTION_REQUIRED = 1 << 2, /* The verb always requires an option to be present */ VERB_COMMAND_MARKER = 1 << 3, /* Header entry with command description */ VERB_GROUP_MARKER = 1 << 4, /* Fake verb entry to separate groups */ + VERB_DEPRECATED = 1 << 5, /* The verb is deprecated and not listed in help. */ } VerbFlags; /* Note: see the comment on struct Option in options.h for why _alignptr_ is required here. */ diff --git a/src/test/test-verbs.c b/src/test/test-verbs.c index 9016a6268c..29143cd23f 100644 --- a/src/test/test-verbs.c +++ b/src/test/test-verbs.c @@ -99,7 +99,7 @@ static const Verb introspect_verbs[] = { .argspec = "FORM1\0FORM2…\0", .help = "Alpha verb", .option_namespace = "test-verbs-alpha" }, { .verb = "beta", .min_args = VERB_ANY, .max_args = VERB_ANY, .dispatch = noop_dispatcher, .help = "Beta verb" }, - { .verb = "gamma", .flags = VERB_OPTION_REQUIRED, + { .verb = "gamma", .flags = VERB_OPTION_REQUIRED | VERB_DEPRECATED, .min_args = VERB_ANY, .max_args = VERB_ANY, .dispatch = noop_dispatcher, .help = "Gamma verb", .footer = "Gamma footer" }, {} From a35f5d5ebac11bd0649c53c14d2c11ecf12e6c40 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Zbigniew=20J=C4=99drzejewski-Szmek?= Date: Wed, 26 Aug 2026 10:10:36 +0200 Subject: [PATCH 08/21] udevadm: convert to VERB_COMMAND and add --introspect-cli The verbs declare their subcommand option namespaces, so the introspection data describes the options of every subcommand, nested in the verb objects. The bare version number output (a dracut compatibility requirement) is preserved for --version/-V and the "version" verb. The "lock" verb's argument specification is simplified to "COMMAND" -- other verbs do not mention their options in the argspec either, and the subcommand help states where the options may go. --- src/udev/udevadm.c | 75 ++++++++----------- .../units/TEST-74-AUX-UTILS.introspect-cli.sh | 6 ++ 2 files changed, 36 insertions(+), 45 deletions(-) diff --git a/src/udev/udevadm.c b/src/udev/udevadm.c index 18d554ab1a..24b4ff6cd4 100644 --- a/src/udev/udevadm.c +++ b/src/udev/udevadm.c @@ -2,10 +2,10 @@ #include +#include "sd-json.h" + #include "argv-util.h" #include "dlopen-note.h" -#include "format-table.h" -#include "help-util.h" #include "label-util.h" #include "main-func.h" #include "options.h" @@ -14,51 +14,33 @@ #include "udevd.h" #include "verbs.h" -static int help(void) { - _cleanup_(table_unrefp) Table *verbs = NULL, *options = NULL; - int r; +COMMAND( + "udevadm\0", + "Send control commands or test the device manager.", + .argspec = "COMMAND [COMMAND OPTION…]\0", + .man_pages = "udevadm(8)\0", + .option_namespace = "udevadm", +); - r = verbs_get_help_table(&verbs); - if (r < 0) - return r; +/* All verbs are defined here, right after the COMMAND, so that they are attributed to it. Each + * subcommand parses its own options from the "udevadm-" namespace, see udevadm-*.c. */ +VERB_COMMON_HELP_AUTO_PROGRAM("udevadm"); - r = option_parser_get_help_table_ns("udevadm", &options); - if (r < 0) - return r; +#define UDEVADM_VERB(fn, name, args, help) \ + VERB_SCOPE_NS(, fn, name, "udevadm-" name, args, VERB_ANY, VERB_ANY, 0, help) - (void) table_sync_column_widths(0, verbs, options); - - help_cmdline("[OPTIONS…] COMMAND [COMMAND OPTIONS…]"); - help_abstract("Send control commands or test the device manager."); - - help_section("Commands"); - r = table_print_or_warn(verbs); - if (r < 0) - return r; - - help_section("Options"); - r = table_print_or_warn(options); - if (r < 0) - return r; - - help_man_page_reference("udevadm", "8"); - return 0; -} - -VERB_COMMON_HELP(help); - -VERB_SCOPE(, verb_info_main, "info", "[DEVPATH|FILE]\0", VERB_ANY, VERB_ANY, 0, "Query sysfs or the udev database"); -VERB_SCOPE(, verb_trigger_main, "trigger", "DEVPATH\0", VERB_ANY, VERB_ANY, 0, "Request events from the kernel"); -VERB_SCOPE(, verb_settle_main, "settle", NULL, VERB_ANY, VERB_ANY, 0, "Wait for pending udev events"); -VERB_SCOPE(, verb_control_main, "control", "OPTION\0", VERB_ANY, VERB_ANY, 0, "Control the udev daemon"); -VERB_SCOPE(, verb_monitor_main, "monitor", NULL, VERB_ANY, VERB_ANY, 0, "Listen to kernel and udev events"); -VERB_SCOPE(, verb_test_main, "test", "DEVPATH\0", VERB_ANY, VERB_ANY, 0, "Test an event run"); -VERB_SCOPE(, verb_builtin_main, "test-builtin", "COMMAND DEVPATH\0", VERB_ANY, VERB_ANY, 0, "Test a built-in command"); -VERB_SCOPE(, verb_verify_main, "verify", "[FILE…]\0", VERB_ANY, VERB_ANY, 0, "Verify udev rules files"); -VERB_SCOPE(, verb_cat_main, "cat", "[FILE…]\0", VERB_ANY, VERB_ANY, 0, "Show udev rules files"); -VERB_SCOPE(, verb_wait_main, "wait", "DEVICE [DEVICE…]\0", VERB_ANY, VERB_ANY, 0, "Wait for device or device symlink"); -VERB_SCOPE(, verb_lock_main, "lock", "[OPTIONS…] COMMAND\0", VERB_ANY, VERB_ANY, 0, "Lock a block device"); -VERB_SCOPE(, verb_hwdb_main, "hwdb", NULL, VERB_ANY, VERB_ANY, 0, /* help= */ NULL); /* deprecated */ +UDEVADM_VERB(verb_info_main, "info", "[DEVPATH|FILE]\0", "Query sysfs or the udev database"); +UDEVADM_VERB(verb_trigger_main, "trigger", "DEVPATH\0", "Request events from the kernel"); +UDEVADM_VERB(verb_settle_main, "settle", NULL, "Wait for pending udev events"); +UDEVADM_VERB(verb_control_main, "control", "OPTION\0", "Control the udev daemon"); +UDEVADM_VERB(verb_monitor_main, "monitor", NULL, "Listen to kernel and udev events"); +UDEVADM_VERB(verb_test_main, "test", "DEVPATH\0", "Test an event run"); +UDEVADM_VERB(verb_builtin_main, "test-builtin", "COMMAND DEVPATH\0", "Test a built-in command"); +UDEVADM_VERB(verb_verify_main, "verify", "[FILE…]\0", "Verify udev rules files"); +UDEVADM_VERB(verb_cat_main, "cat", "[FILE…]\0", "Show udev rules files"); +UDEVADM_VERB(verb_wait_main, "wait", "DEVICE [DEVICE…]\0", "Wait for device or device symlink"); +UDEVADM_VERB(verb_lock_main, "lock", "COMMAND\0", "Lock a block device"); +UDEVADM_VERB(verb_hwdb_main, "hwdb", NULL, /* help= */ NULL); /* deprecated */ VERB_NOARG(verb_version_main, "version", /* help= */ NULL); static int verb_version_main(int argc, char *argv[], uintptr_t _data, void *userdata) { @@ -78,7 +60,7 @@ static int parse_argv(int argc, char *argv[], char ***remaining_args) { OPTION_NAMESPACE("udevadm"): {} OPTION_COMMON_HELP: - return help(); + return command_print_help_name("udevadm"); OPTION_COMMON_VERSION_WITH_HIDDEN_V: return print_version(); @@ -86,6 +68,9 @@ static int parse_argv(int argc, char *argv[], char ***remaining_args) { OPTION('d', "debug", NULL, "Enable debug logging"): log_set_max_level(LOG_DEBUG); break; + + OPTION_COMMON_INTROSPECT_CLI: + return introspect_cli(SD_JSON_FORMAT_OFF); } *remaining_args = option_parser_get_args(&opts); diff --git a/test/units/TEST-74-AUX-UTILS.introspect-cli.sh b/test/units/TEST-74-AUX-UTILS.introspect-cli.sh index c80b1c41eb..30c8762905 100755 --- a/test/units/TEST-74-AUX-UTILS.introspect-cli.sh +++ b/test/units/TEST-74-AUX-UTILS.introspect-cli.sh @@ -143,6 +143,7 @@ INTROSPECTABLE=( systemd-vmspawn systemd-vpick timedatectl + udevadm updatectl userdbctl v4l_id @@ -208,6 +209,11 @@ fi systemd-id128 --introspect-cli | jq -e \ '.commands[0].verbs | map(.names[0]) | contains(["new", "machine-id", "show", "help"])' +# udevadm's verbs carry their own options, reported recursively +udevadm --introspect-cli | jq -e \ + '.commands[] | select(.names[0] == "udevadm") | .verbs[] | select(.names[0] == "info") | + [.options[].names[-1]] | contains(["--query", "--json", "--no-pager"])' + # systemd is optionally a multicall binary that also provides systemd-executor if [[ "$(readlink "$(command -v systemd-executor)" 2>/dev/null)" == systemd ]]; then systemd --introspect-cli | jq -e \ From 96b091a6b4d8d60590460b214c0af34190396c9f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Zbigniew=20J=C4=99drzejewski-Szmek?= Date: Wed, 26 Aug 2026 10:13:39 +0200 Subject: [PATCH 09/21] udevadm-cat: use common verb help and add --introspect-cli The custom help() is replaced by the common verb help, which derives the synopsis, abstract, and option list from the verb declaration in udevadm.c and the "udevadm-cat" option namespace. --- src/udev/udevadm-cat.c | 29 +++++++---------------------- 1 file changed, 7 insertions(+), 22 deletions(-) diff --git a/src/udev/udevadm-cat.c b/src/udev/udevadm-cat.c index fcce76663e..936455c24a 100644 --- a/src/udev/udevadm-cat.c +++ b/src/udev/udevadm-cat.c @@ -1,9 +1,9 @@ /* SPDX-License-Identifier: GPL-2.0-or-later */ +#include "sd-json.h" + #include "alloc-util.h" #include "conf-files.h" -#include "format-table.h" -#include "help-util.h" #include "log.h" #include "options.h" #include "parse-argument.h" @@ -11,6 +11,7 @@ #include "static-destruct.h" #include "udevadm.h" #include "udevadm-util.h" +#include "verbs.h" static char *arg_root = NULL; static CatFlags arg_cat_flags = 0; @@ -18,25 +19,6 @@ static bool arg_config = false; STATIC_DESTRUCTOR_REGISTER(arg_root, freep); -static int help(void) { - _cleanup_(table_unrefp) Table *options = NULL; - int r; - - r = option_parser_get_help_table_ns("udevadm-cat", &options); - if (r < 0) - return r; - - help_cmdline("cat [OPTIONS...] [FILE...]"); - help_abstract("Show udev rules files."); - help_section("Options"); - r = table_print_or_warn(options); - if (r < 0) - return r; - - help_man_page_reference("udevadm", "8"); - return 0; -} - static int parse_argv(int argc, char *argv[], char ***remaining_args) { int r; @@ -52,7 +34,7 @@ static int parse_argv(int argc, char *argv[], char ***remaining_args) { OPTION_NAMESPACE("udevadm-cat"): {} OPTION_COMMON_HELP: - return help(); + return command_print_verb_help("cat"); OPTION_COMMON_VERSION_WITH_HIDDEN_V: return print_version(); @@ -73,6 +55,9 @@ static int parse_argv(int argc, char *argv[], char ***remaining_args) { "Show udev.conf rather than udev rules files"): arg_config = true; break; + + OPTION_COMMON_INTROSPECT_CLI: + return introspect_cli(SD_JSON_FORMAT_OFF); } if (arg_config && option_parser_get_n_args(&opts) > 0) From da4f84ae4a8c35d97ebd51b7b53383751d680c24 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Zbigniew=20J=C4=99drzejewski-Szmek?= Date: Wed, 26 Aug 2026 10:14:51 +0200 Subject: [PATCH 10/21] udevadm-control: use common verb help and add --introspect-cli MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The custom help() is replaced by the common verb help, which derives the synopsis, abstract, and option list from the verb declaration in udevadm.c and the "udevadm-control" option namespace. The synopsis is now "udevadm [OPTION…] control OPTION…". --- src/udev/udevadm-control.c | 29 +++++++---------------------- src/udev/udevadm.c | 28 ++++++++++++++-------------- 2 files changed, 21 insertions(+), 36 deletions(-) diff --git a/src/udev/udevadm-control.c b/src/udev/udevadm-control.c index 54bf1a003b..d1c0ac4e6d 100644 --- a/src/udev/udevadm-control.c +++ b/src/udev/udevadm-control.c @@ -2,9 +2,9 @@ #include +#include "sd-json.h" + #include "creds-util.h" -#include "format-table.h" -#include "help-util.h" #include "log.h" #include "options.h" #include "parse-argument.h" @@ -16,6 +16,7 @@ #include "udev-varlink.h" #include "udevadm.h" #include "varlink-util.h" +#include "verbs.h" #include "virt.h" static char **arg_env = NULL; @@ -45,25 +46,6 @@ static bool arg_has_control_commands(void) { arg_revert; } -static int help(void) { - _cleanup_(table_unrefp) Table *options = NULL; - int r; - - r = option_parser_get_help_table_ns("udevadm-control", &options); - if (r < 0) - return r; - - help_cmdline("control OPTION"); - help_abstract("Control the udev daemon."); - help_section("Options"); - r = table_print_or_warn(options); - if (r < 0) - return r; - - help_man_page_reference("udevadm", "8"); - return 0; -} - static int parse_argv(int argc, char *argv[]) { int r; @@ -78,7 +60,7 @@ static int parse_argv(int argc, char *argv[]) { OPTION_NAMESPACE("udevadm-control"): {} OPTION_COMMON_HELP: - return help(); + return command_print_verb_help("control"); OPTION('V', "version", NULL, "Show package version"): return print_version(); @@ -155,6 +137,9 @@ static int parse_argv(int argc, char *argv[]) { OPTION_LONG("load-credentials", NULL, "Load udev rules from credentials"): arg_load_credentials = true; break; + + OPTION_COMMON_INTROSPECT_CLI: + return introspect_cli(SD_JSON_FORMAT_OFF); } if (!arg_has_control_commands() && !arg_load_credentials) diff --git a/src/udev/udevadm.c b/src/udev/udevadm.c index 24b4ff6cd4..50bfd783e6 100644 --- a/src/udev/udevadm.c +++ b/src/udev/udevadm.c @@ -26,21 +26,21 @@ COMMAND( * subcommand parses its own options from the "udevadm-" namespace, see udevadm-*.c. */ VERB_COMMON_HELP_AUTO_PROGRAM("udevadm"); -#define UDEVADM_VERB(fn, name, args, help) \ - VERB_SCOPE_NS(, fn, name, "udevadm-" name, args, VERB_ANY, VERB_ANY, 0, help) +#define UDEVADM_VERB(fn, name, args, flags, help, footer) \ + VERB_SCOPE_NS_FULL(, fn, name, "udevadm-" name, args, VERB_ANY, VERB_ANY, flags, /* dat= */ 0, help, footer) -UDEVADM_VERB(verb_info_main, "info", "[DEVPATH|FILE]\0", "Query sysfs or the udev database"); -UDEVADM_VERB(verb_trigger_main, "trigger", "DEVPATH\0", "Request events from the kernel"); -UDEVADM_VERB(verb_settle_main, "settle", NULL, "Wait for pending udev events"); -UDEVADM_VERB(verb_control_main, "control", "OPTION\0", "Control the udev daemon"); -UDEVADM_VERB(verb_monitor_main, "monitor", NULL, "Listen to kernel and udev events"); -UDEVADM_VERB(verb_test_main, "test", "DEVPATH\0", "Test an event run"); -UDEVADM_VERB(verb_builtin_main, "test-builtin", "COMMAND DEVPATH\0", "Test a built-in command"); -UDEVADM_VERB(verb_verify_main, "verify", "[FILE…]\0", "Verify udev rules files"); -UDEVADM_VERB(verb_cat_main, "cat", "[FILE…]\0", "Show udev rules files"); -UDEVADM_VERB(verb_wait_main, "wait", "DEVICE [DEVICE…]\0", "Wait for device or device symlink"); -UDEVADM_VERB(verb_lock_main, "lock", "COMMAND\0", "Lock a block device"); -UDEVADM_VERB(verb_hwdb_main, "hwdb", NULL, /* help= */ NULL); /* deprecated */ +UDEVADM_VERB(verb_info_main, "info", "[DEVPATH|FILE]\0", 0, "Query sysfs or the udev database", NULL); +UDEVADM_VERB(verb_trigger_main, "trigger", "DEVPATH\0", 0, "Request events from the kernel", NULL); +UDEVADM_VERB(verb_settle_main, "settle", NULL, 0, "Wait for pending udev events", NULL); +UDEVADM_VERB(verb_control_main, "control", NULL, VERB_OPTION_REQUIRED, "Control the udev daemon", NULL); +UDEVADM_VERB(verb_monitor_main, "monitor", NULL, 0, "Listen to kernel and udev events", NULL); +UDEVADM_VERB(verb_test_main, "test", "DEVPATH\0", 0, "Test an event run", NULL); +UDEVADM_VERB(verb_builtin_main, "test-builtin", "COMMAND DEVPATH\0", 0, "Test a built-in command", NULL); +UDEVADM_VERB(verb_verify_main, "verify", "[FILE…]\0", 0, "Verify udev rules files", NULL); +UDEVADM_VERB(verb_cat_main, "cat", "[FILE…]\0", 0, "Show udev rules files", NULL); +UDEVADM_VERB(verb_wait_main, "wait", "DEVICE [DEVICE…]\0", 0, "Wait for device or device symlink", NULL); +UDEVADM_VERB(verb_lock_main, "lock", "COMMAND", 0, "Lock a block device", NULL); +UDEVADM_VERB(verb_hwdb_main, "hwdb", NULL, 0, /* help= */ NULL, NULL); /* deprecated */ VERB_NOARG(verb_version_main, "version", /* help= */ NULL); static int verb_version_main(int argc, char *argv[], uintptr_t _data, void *userdata) { From 0ba8647c6d4bf9fe46b52b7635acf1effa48c7ee Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Zbigniew=20J=C4=99drzejewski-Szmek?= Date: Wed, 26 Aug 2026 10:14:51 +0200 Subject: [PATCH 11/21] udevadm-hwdb: use common verb help and add --introspect-cli The custom help() is replaced by the common verb help, which derives the synopsis, abstract, and option list from the verb declaration in udevadm.c and the "udevadm-control" option namespace. The new VERB_DEPRECATED flag is used. --- src/udev/udevadm-hwdb.c | 31 ++++--------------- src/udev/udevadm.c | 3 +- .../units/TEST-74-AUX-UTILS.introspect-cli.sh | 4 +++ 3 files changed, 12 insertions(+), 26 deletions(-) diff --git a/src/udev/udevadm-hwdb.c b/src/udev/udevadm-hwdb.c index 0a38e6ee06..143a3a621d 100644 --- a/src/udev/udevadm-hwdb.c +++ b/src/udev/udevadm-hwdb.c @@ -1,13 +1,12 @@ /* SPDX-License-Identifier: LGPL-2.1-or-later */ -#include +#include "sd-json.h" -#include "format-table.h" -#include "help-util.h" #include "hwdb-util.h" #include "log.h" #include "options.h" #include "udevadm.h" +#include "verbs.h" static const char *arg_test = NULL; static const char *arg_root = NULL; @@ -15,27 +14,6 @@ static const char *arg_hwdb_bin_dir = NULL; static bool arg_update = false; static bool arg_strict = false; -static int help(void) { - _cleanup_(table_unrefp) Table *options = NULL; - int r; - - r = option_parser_get_help_table_ns("udevadm-hwdb", &options); - if (r < 0) - return r; - - help_cmdline("hwdb [OPTIONS]"); - help_abstract("Update or query the hardware database."); - help_section("Options"); - r = table_print_or_warn(options); - if (r < 0) - return r; - - printf("\nNOTE:\n" - "The sub-command 'hwdb' is deprecated, and is left for backwards compatibility.\n" - "Please use systemd-hwdb instead.\n"); - return 0; -} - static int parse_argv(int argc, char *argv[]) { assert(argc >= 0); assert(argv); @@ -48,7 +26,7 @@ static int parse_argv(int argc, char *argv[]) { OPTION_NAMESPACE("udevadm-hwdb"): {} OPTION_COMMON_HELP: - return help(); + return command_print_verb_help("hwdb"); OPTION('V', "version", NULL, "Show package version"): return print_version(); @@ -74,6 +52,9 @@ static int parse_argv(int argc, char *argv[]) { OPTION('r', "root", "PATH", "Alternative root path in the filesystem"): arg_root = opts.arg; break; + + OPTION_COMMON_INTROSPECT_CLI: + return introspect_cli(SD_JSON_FORMAT_OFF); } return 1; diff --git a/src/udev/udevadm.c b/src/udev/udevadm.c index 50bfd783e6..59b4fbe5fd 100644 --- a/src/udev/udevadm.c +++ b/src/udev/udevadm.c @@ -40,7 +40,8 @@ UDEVADM_VERB(verb_verify_main, "verify", "[FILE…]\0", 0, UDEVADM_VERB(verb_cat_main, "cat", "[FILE…]\0", 0, "Show udev rules files", NULL); UDEVADM_VERB(verb_wait_main, "wait", "DEVICE [DEVICE…]\0", 0, "Wait for device or device symlink", NULL); UDEVADM_VERB(verb_lock_main, "lock", "COMMAND", 0, "Lock a block device", NULL); -UDEVADM_VERB(verb_hwdb_main, "hwdb", NULL, 0, /* help= */ NULL, NULL); /* deprecated */ +UDEVADM_VERB(verb_hwdb_main, "hwdb", NULL, VERB_DEPRECATED, "Update or query the hardware database", + "Verb 'hwdb' is deprecated. Use systemd-hwdb instead."); VERB_NOARG(verb_version_main, "version", /* help= */ NULL); static int verb_version_main(int argc, char *argv[], uintptr_t _data, void *userdata) { diff --git a/test/units/TEST-74-AUX-UTILS.introspect-cli.sh b/test/units/TEST-74-AUX-UTILS.introspect-cli.sh index 30c8762905..b7cd7ed9bc 100755 --- a/test/units/TEST-74-AUX-UTILS.introspect-cli.sh +++ b/test/units/TEST-74-AUX-UTILS.introspect-cli.sh @@ -214,6 +214,10 @@ udevadm --introspect-cli | jq -e \ '.commands[] | select(.names[0] == "udevadm") | .verbs[] | select(.names[0] == "info") | [.options[].names[-1]] | contains(["--query", "--json", "--no-pager"])' +udevadm --introspect-cli | jq -e \ + '.commands[] | select(.names[0] == "udevadm") | .verbs[] | + select(.names[0] == "hwdb") | .isDeprecated == true' + # systemd is optionally a multicall binary that also provides systemd-executor if [[ "$(readlink "$(command -v systemd-executor)" 2>/dev/null)" == systemd ]]; then systemd --introspect-cli | jq -e \ From e939427d5d37bfb821136a97e427d2b1503cd940 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Zbigniew=20J=C4=99drzejewski-Szmek?= Date: Wed, 26 Aug 2026 10:15:33 +0200 Subject: [PATCH 12/21] udevadm-info: use common verb help and add --introspect-cli The arg_no_pager variable is lifted to udevadm, so that it can be referenced in the command structure. It's only used for 'udevadm info', but that's OK. We can later generalize it if wanted. The custom help() is replaced by the common verb help, which derives the synopsis, abstract, and option list from the verb declaration in udevadm.c and the "udevadm-info" option namespace. --help now goes through the pager, like other commands with --no-pager. --- src/udev/udevadm-info.c | 28 +++++----------------------- src/udev/udevadm.c | 4 ++++ src/udev/udevadm.h | 2 ++ 3 files changed, 11 insertions(+), 23 deletions(-) diff --git a/src/udev/udevadm-info.c b/src/udev/udevadm-info.c index 439cdfd743..4fb9735e47 100644 --- a/src/udev/udevadm-info.c +++ b/src/udev/udevadm-info.c @@ -19,9 +19,7 @@ #include "errno-util.h" #include "fd-util.h" #include "fileio.h" -#include "format-table.h" #include "glyph-util.h" -#include "help-util.h" #include "options.h" #include "pager.h" #include "parse-argument.h" @@ -34,6 +32,7 @@ #include "udev-util.h" #include "udevadm.h" #include "udevadm-util.h" +#include "verbs.h" typedef enum ActionType { ACTION_QUERY, @@ -60,7 +59,6 @@ static bool arg_export = false; static bool arg_value = false; static const char *arg_export_prefix = NULL; static usec_t arg_wait_for_initialization_timeout = 0; -static PagerFlags arg_pager_flags = 0; static sd_json_format_flags_t arg_json_format_flags = SD_JSON_FORMAT_OFF; static ActionType arg_action_type = ACTION_QUERY; static QueryType arg_query = QUERY_ALL; @@ -806,25 +804,6 @@ static int query_device(QueryType query, sd_device* device) { } } -static int help(void) { - _cleanup_(table_unrefp) Table *options = NULL; - int r; - - r = option_parser_get_help_table_ns("udevadm-info", &options); - if (r < 0) - return r; - - help_cmdline("info [OPTIONS] [DEVPATH|FILE]"); - help_abstract("Query sysfs or the udev database."); - help_section("Options"); - r = table_print_or_warn(options); - if (r < 0) - return r; - - help_man_page_reference("udevadm", "8"); - return 0; -} - static int draw_tree( sd_device *parent, sd_device *const array[], size_t n, @@ -996,7 +975,7 @@ static int parse_argv(int argc, char *argv[]) { OPTION_NAMESPACE("udevadm-info"): {} OPTION_COMMON_HELP: - return help(); + return command_print_verb_help("info"); OPTION('V', "version", NULL, "Show package version"): return print_version(); @@ -1179,6 +1158,9 @@ static int parse_argv(int argc, char *argv[]) { "Query devices that are not initialized yet"): arg_initialized_match = MATCH_INITIALIZED_NO; break; + + OPTION_COMMON_INTROSPECT_CLI: + return introspect_cli(arg_json_format_flags); } r = strv_extend_strv(&arg_devices, option_parser_get_args(&opts), /* filter_duplicates= */ false); diff --git a/src/udev/udevadm.c b/src/udev/udevadm.c index 59b4fbe5fd..8c3aedda05 100644 --- a/src/udev/udevadm.c +++ b/src/udev/udevadm.c @@ -9,17 +9,21 @@ #include "label-util.h" #include "main-func.h" #include "options.h" +#include "pager.h" #include "udev-util.h" #include "udevadm.h" #include "udevd.h" #include "verbs.h" +PagerFlags arg_pager_flags = 0; + COMMAND( "udevadm\0", "Send control commands or test the device manager.", .argspec = "COMMAND [COMMAND OPTION…]\0", .man_pages = "udevadm(8)\0", .option_namespace = "udevadm", + .pager_flags = &arg_pager_flags, ); /* All verbs are defined here, right after the COMMAND, so that they are attributed to it. Each diff --git a/src/udev/udevadm.h b/src/udev/udevadm.h index 224c0ac4bf..2af2bc646b 100644 --- a/src/udev/udevadm.h +++ b/src/udev/udevadm.h @@ -3,6 +3,8 @@ #include "forward.h" +extern PagerFlags arg_pager_flags; + int verb_cat_main(int argc, char *argv[], uintptr_t _data, void *userdata); int verb_info_main(int argc, char *argv[], uintptr_t _data, void *userdata); int verb_trigger_main(int argc, char *argv[], uintptr_t _data, void *userdata); From d8b733d6ddd93170ddff46e20073eba13a73e74f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Zbigniew=20J=C4=99drzejewski-Szmek?= Date: Thu, 27 Aug 2026 11:58:28 +0200 Subject: [PATCH 13/21] udevadm-lock: use common verb help and add --introspect-cli MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The custom help() is replaced by the common verb help, which derives the synopsis, abstract, and option list from the verb declaration in udevadm.c and the "udevadm-control" option namespace. The synopsis makes use of the new nulstr-argspec functionality: > udevadm [OPTION…] lock [OPTION…] COMMAND > udevadm [OPTION…] lock [OPTION…] --print --- src/udev/udevadm-lock.c | 32 +++++-------------- src/udev/udevadm.c | 22 ++++++------- .../units/TEST-74-AUX-UTILS.introspect-cli.sh | 4 +++ 3 files changed, 23 insertions(+), 35 deletions(-) diff --git a/src/udev/udevadm-lock.c b/src/udev/udevadm-lock.c index fd4b6a9059..f0cc57e43c 100644 --- a/src/udev/udevadm-lock.c +++ b/src/udev/udevadm-lock.c @@ -4,14 +4,14 @@ #include #include +#include "sd-json.h" + #include "blockdev-util.h" #include "device-util.h" #include "fd-util.h" #include "fdset.h" -#include "format-table.h" #include "glyph-util.h" #include "hash-funcs.h" -#include "help-util.h" #include "lock-util.h" #include "options.h" #include "path-util.h" @@ -23,6 +23,7 @@ #include "strv.h" #include "time-util.h" #include "udevadm.h" +#include "verbs.h" static usec_t arg_timeout_usec = USEC_INFINITY; static char **arg_devices = NULL; @@ -34,26 +35,6 @@ STATIC_DESTRUCTOR_REGISTER(arg_devices, strv_freep); STATIC_DESTRUCTOR_REGISTER(arg_backing, strv_freep); STATIC_DESTRUCTOR_REGISTER(arg_cmdline, strv_freep); -static int help(void) { - _cleanup_(table_unrefp) Table *options = NULL; - int r; - - r = option_parser_get_help_table_ns("udevadm-lock", &options); - if (r < 0) - return r; - - help_cmdline("lock [OPTIONS...] COMMAND"); - help_cmdline("lock [OPTIONS...] --print"); - help_abstract("Lock a block device and run a command."); - help_section("Options"); - r = table_print_or_warn(options); - if (r < 0) - return r; - - help_man_page_reference("udevadm", "8"); - return 0; -} - static int parse_argv(int argc, char *argv[]) { int r; @@ -68,7 +49,7 @@ static int parse_argv(int argc, char *argv[]) { OPTION_NAMESPACE("udevadm-lock"): {} OPTION_COMMON_HELP: - return help(); + return command_print_verb_help("lock"); OPTION('V', "version", NULL, "Show package version"): return print_version(); @@ -97,9 +78,12 @@ static int parse_argv(int argc, char *argv[]) { return log_error_errno(r, "Failed to parse --timeout= parameter: %s", opts.arg); break; - OPTION('p', "print", NULL, "Only show which block device the lock would be taken on"): + OPTION('p', "print", NULL, "Only show which device would be locked, without running the command"): arg_print = true; break; + + OPTION_COMMON_INTROSPECT_CLI: + return introspect_cli(SD_JSON_FORMAT_OFF); } char **args = option_parser_get_args(&opts); diff --git a/src/udev/udevadm.c b/src/udev/udevadm.c index 8c3aedda05..359c04b30f 100644 --- a/src/udev/udevadm.c +++ b/src/udev/udevadm.c @@ -33,17 +33,17 @@ VERB_COMMON_HELP_AUTO_PROGRAM("udevadm"); #define UDEVADM_VERB(fn, name, args, flags, help, footer) \ VERB_SCOPE_NS_FULL(, fn, name, "udevadm-" name, args, VERB_ANY, VERB_ANY, flags, /* dat= */ 0, help, footer) -UDEVADM_VERB(verb_info_main, "info", "[DEVPATH|FILE]\0", 0, "Query sysfs or the udev database", NULL); -UDEVADM_VERB(verb_trigger_main, "trigger", "DEVPATH\0", 0, "Request events from the kernel", NULL); -UDEVADM_VERB(verb_settle_main, "settle", NULL, 0, "Wait for pending udev events", NULL); -UDEVADM_VERB(verb_control_main, "control", NULL, VERB_OPTION_REQUIRED, "Control the udev daemon", NULL); -UDEVADM_VERB(verb_monitor_main, "monitor", NULL, 0, "Listen to kernel and udev events", NULL); -UDEVADM_VERB(verb_test_main, "test", "DEVPATH\0", 0, "Test an event run", NULL); -UDEVADM_VERB(verb_builtin_main, "test-builtin", "COMMAND DEVPATH\0", 0, "Test a built-in command", NULL); -UDEVADM_VERB(verb_verify_main, "verify", "[FILE…]\0", 0, "Verify udev rules files", NULL); -UDEVADM_VERB(verb_cat_main, "cat", "[FILE…]\0", 0, "Show udev rules files", NULL); -UDEVADM_VERB(verb_wait_main, "wait", "DEVICE [DEVICE…]\0", 0, "Wait for device or device symlink", NULL); -UDEVADM_VERB(verb_lock_main, "lock", "COMMAND", 0, "Lock a block device", NULL); +UDEVADM_VERB(verb_info_main, "info", "[DEVPATH|FILE]\0", 0, "Query sysfs or the udev database", NULL); +UDEVADM_VERB(verb_trigger_main, "trigger", "DEVPATH\0", 0, "Request events from the kernel", NULL); +UDEVADM_VERB(verb_settle_main, "settle", NULL, 0, "Wait for pending udev events", NULL); +UDEVADM_VERB(verb_control_main, "control", NULL, VERB_OPTION_REQUIRED, "Control the udev daemon", NULL); +UDEVADM_VERB(verb_monitor_main, "monitor", NULL, 0, "Listen to kernel and udev events", NULL); +UDEVADM_VERB(verb_test_main, "test", "DEVPATH\0", 0, "Test an event run", NULL); +UDEVADM_VERB(verb_builtin_main, "test-builtin", "COMMAND DEVPATH\0", 0, "Test a built-in command", NULL); +UDEVADM_VERB(verb_verify_main, "verify", "[FILE…]\0", 0, "Verify udev rules files", NULL); +UDEVADM_VERB(verb_cat_main, "cat", "[FILE…]\0", 0, "Show udev rules files", NULL); +UDEVADM_VERB(verb_wait_main, "wait", "DEVICE [DEVICE…]\0", 0, "Wait for device or device symlink", NULL); +UDEVADM_VERB(verb_lock_main, "lock", "COMMAND\0--print\0", 0, "Lock a block device and run a command", NULL); UDEVADM_VERB(verb_hwdb_main, "hwdb", NULL, VERB_DEPRECATED, "Update or query the hardware database", "Verb 'hwdb' is deprecated. Use systemd-hwdb instead."); diff --git a/test/units/TEST-74-AUX-UTILS.introspect-cli.sh b/test/units/TEST-74-AUX-UTILS.introspect-cli.sh index b7cd7ed9bc..40116bd870 100755 --- a/test/units/TEST-74-AUX-UTILS.introspect-cli.sh +++ b/test/units/TEST-74-AUX-UTILS.introspect-cli.sh @@ -218,6 +218,10 @@ udevadm --introspect-cli | jq -e \ '.commands[] | select(.names[0] == "udevadm") | .verbs[] | select(.names[0] == "hwdb") | .isDeprecated == true' +# udevadm print has multiple argspecs. Check that we print them. +udevadm lock -h | grep -E '^> udevadm .* lock .* COMMAND$' +udevadm lock -h | grep -E '^> udevadm .* lock .* --print$' + # systemd is optionally a multicall binary that also provides systemd-executor if [[ "$(readlink "$(command -v systemd-executor)" 2>/dev/null)" == systemd ]]; then systemd --introspect-cli | jq -e \ From d77ecb5c376985a79e9fd9a3d0bc8be502adcc9c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Zbigniew=20J=C4=99drzejewski-Szmek?= Date: Wed, 26 Aug 2026 10:16:01 +0200 Subject: [PATCH 14/21] udevadm-monitor: use common verb help and add --introspect-cli The custom help() is replaced by the common verb help, which derives the synopsis, abstract, and option list from the verb declaration in udevadm.c and the "udevadm-monitor" option namespace. --- src/udev/udevadm-monitor.c | 28 ++++++---------------------- 1 file changed, 6 insertions(+), 22 deletions(-) diff --git a/src/udev/udevadm-monitor.c b/src/udev/udevadm-monitor.c index 76c9d16d1f..ef936cb7d1 100644 --- a/src/udev/udevadm-monitor.c +++ b/src/udev/udevadm-monitor.c @@ -2,21 +2,21 @@ #include "sd-device.h" #include "sd-event.h" +#include "sd-json.h" #include "alloc-util.h" #include "device-monitor-private.h" #include "device-private.h" #include "device-util.h" -#include "format-table.h" #include "format-util.h" #include "hashmap.h" -#include "help-util.h" #include "options.h" #include "set.h" #include "static-destruct.h" #include "string-util.h" #include "time-util.h" #include "udevadm.h" +#include "verbs.h" #include "virt.h" static bool arg_show_property = false; @@ -99,25 +99,6 @@ static int setup_monitor(MonitorNetlinkGroup sender, sd_event *event, sd_device_ return 0; } -static int help(void) { - _cleanup_(table_unrefp) Table *options = NULL; - int r; - - r = option_parser_get_help_table_ns("udevadm-monitor", &options); - if (r < 0) - return r; - - help_cmdline("monitor [OPTIONS]"); - help_abstract("Listen to kernel and udev events."); - help_section("Options"); - r = table_print_or_warn(options); - if (r < 0) - return r; - - help_man_page_reference("udevadm", "8"); - return 0; -} - static int parse_argv(int argc, char *argv[]) { int r; @@ -132,7 +113,7 @@ static int parse_argv(int argc, char *argv[]) { OPTION_NAMESPACE("udevadm-monitor"): {} OPTION_COMMON_HELP: - return help(); + return command_print_verb_help("monitor"); OPTION('V', "version", NULL, "Show package version"): return print_version(); @@ -182,6 +163,9 @@ static int parse_argv(int argc, char *argv[]) { if (r < 0) return log_oom(); break; + + OPTION_COMMON_INTROSPECT_CLI: + return introspect_cli(SD_JSON_FORMAT_OFF); } if (!arg_print_kernel && !arg_print_udev) { From 4a7a683d71b7ad5ace6e9d87c84237b1124f4bf5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Zbigniew=20J=C4=99drzejewski-Szmek?= Date: Wed, 26 Aug 2026 10:16:01 +0200 Subject: [PATCH 15/21] udevadm-settle: use common verb help and add --introspect-cli The custom help() is replaced by the common verb help, which derives the synopsis, abstract, and option list from the verb declaration in udevadm.c and the "udevadm-settle" option namespace. --- src/udev/udevadm-settle.c | 28 ++++++---------------------- 1 file changed, 6 insertions(+), 22 deletions(-) diff --git a/src/udev/udevadm-settle.c b/src/udev/udevadm-settle.c index 4398f40e72..dfd00e4b0d 100644 --- a/src/udev/udevadm-settle.c +++ b/src/udev/udevadm-settle.c @@ -8,13 +8,12 @@ #include "sd-bus.h" #include "sd-event.h" +#include "sd-json.h" #include "sd-login.h" #include "sd-messages.h" #include "alloc-util.h" #include "bus-util.h" -#include "format-table.h" -#include "help-util.h" #include "options.h" #include "path-util.h" #include "string-util.h" @@ -24,30 +23,12 @@ #include "udevadm.h" #include "udevadm-util.h" #include "unit-def.h" +#include "verbs.h" #include "virt.h" static usec_t arg_timeout_usec = 120 * USEC_PER_SEC; static const char *arg_exists = NULL; -static int help(void) { - _cleanup_(table_unrefp) Table *options = NULL; - int r; - - r = option_parser_get_help_table_ns("udevadm-settle", &options); - if (r < 0) - return r; - - help_cmdline("settle [OPTIONS]"); - help_abstract("Wait for pending udev events."); - help_section("Options"); - r = table_print_or_warn(options); - if (r < 0) - return r; - - help_man_page_reference("udevadm", "8"); - return 0; -} - static int parse_argv(int argc, char *argv[]) { int r; @@ -62,7 +43,7 @@ static int parse_argv(int argc, char *argv[]) { OPTION_NAMESPACE("udevadm-settle"): {} OPTION_COMMON_HELP: - return help(); + return command_print_verb_help("settle"); OPTION('V', "version", NULL, "Show package version"): return print_version(); @@ -86,6 +67,9 @@ static int parse_argv(int argc, char *argv[]) { return log_info_errno(SYNTHETIC_ERRNO(EINVAL), "Option -%c no longer supported.", opts.opt->short_code); + + OPTION_COMMON_INTROSPECT_CLI: + return introspect_cli(SD_JSON_FORMAT_OFF); } if (option_parser_get_n_args(&opts) > 0) From 2ac1195458cec451ac1ae6f8fbee41215681f454 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Zbigniew=20J=C4=99drzejewski-Szmek?= Date: Wed, 26 Aug 2026 10:16:50 +0200 Subject: [PATCH 16/21] udevadm-test: use common verb help and add --introspect-cli The custom help() is replaced by the common verb help, which derives the synopsis, abstract, and option list from the verb declaration in udevadm.c and the "udevadm-test" option namespace. --- src/udev/udevadm-test.c | 27 +++++---------------------- 1 file changed, 5 insertions(+), 22 deletions(-) diff --git a/src/udev/udevadm-test.c b/src/udev/udevadm-test.c index ba8217c8d3..a496f81b99 100644 --- a/src/udev/udevadm-test.c +++ b/src/udev/udevadm-test.c @@ -11,8 +11,6 @@ #include "alloc-util.h" #include "device-private.h" -#include "format-table.h" -#include "help-util.h" #include "log.h" #include "options.h" #include "parse-argument.h" @@ -24,6 +22,7 @@ #include "udev-rules.h" #include "udevadm.h" #include "udevadm-util.h" +#include "verbs.h" static sd_device_action_t arg_action = SD_DEVICE_ADD; static ResolveNameTiming arg_resolve_name_timing = RESOLVE_NAME_EARLY; @@ -34,25 +33,6 @@ static sd_json_format_flags_t arg_json_format_flags = SD_JSON_FORMAT_OFF; STATIC_DESTRUCTOR_REGISTER(arg_extra_rules_dir, strv_freep); -static int help(void) { - _cleanup_(table_unrefp) Table *options = NULL; - int r; - - r = option_parser_get_help_table_ns("udevadm-test", &options); - if (r < 0) - return r; - - help_cmdline("test [OPTIONS] DEVPATH"); - help_abstract("Test an event run."); - help_section("Options"); - r = table_print_or_warn(options); - if (r < 0) - return r; - - help_man_page_reference("udevadm", "8"); - return 0; -} - static int parse_argv(int argc, char *argv[]) { int r; @@ -67,7 +47,7 @@ static int parse_argv(int argc, char *argv[]) { OPTION_NAMESPACE("udevadm-test"): {} OPTION_COMMON_HELP: - return help(); + return command_print_verb_help("test"); OPTION('V', "version", NULL, "Show package version"): return print_version(); @@ -106,6 +86,9 @@ static int parse_argv(int argc, char *argv[]) { if (r <= 0) return r; break; + + OPTION_COMMON_INTROSPECT_CLI: + return introspect_cli(arg_json_format_flags); } char **args = option_parser_get_args(&opts); From 580587643a2860874085c59f41dc8a3d76761f60 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Zbigniew=20J=C4=99drzejewski-Szmek?= Date: Wed, 26 Aug 2026 10:16:50 +0200 Subject: [PATCH 17/21] udevadm-test-builtin: use common verb help and add --introspect-cli The custom help() is replaced by the common verb help, which derives the synopsis, abstract, and option list from the verb declaration in udevadm.c and the "udevadm-test-builtin" option namespace. The list of built-in commands is appended after the generic help. The man page reference is now printed before the command list. It'd be somewhat nicer to have it _after_, but that'd require a lot of plumbing to split the help generated in two and just doesn't seem worth effort. The list of commands is now printed to stdout (it went to stderr before), arguably a backportable bugfix. --- src/udev/udev-builtin.c | 2 +- src/udev/udevadm-test-builtin.c | 35 ++++++++++++--------------------- 2 files changed, 14 insertions(+), 23 deletions(-) diff --git a/src/udev/udev-builtin.c b/src/udev/udev-builtin.c index de284d60fe..7ad8451347 100644 --- a/src/udev/udev-builtin.c +++ b/src/udev/udev-builtin.c @@ -76,7 +76,7 @@ void udev_builtin_reload(UdevReloadFlags flags) { void udev_builtin_list(void) { FOREACH_ELEMENT(b, builtins) if (*b) - fprintf(stderr, " %-14s %s\n", (*b)->name, (*b)->help); + printf(" %-14s %s\n", (*b)->name, (*b)->help); } const char* udev_builtin_name(UdevBuiltinCommand cmd) { diff --git a/src/udev/udevadm-test-builtin.c b/src/udev/udevadm-test-builtin.c index 3de1366b59..cd8985cae7 100644 --- a/src/udev/udevadm-test-builtin.c +++ b/src/udev/udevadm-test-builtin.c @@ -1,39 +1,21 @@ /* SPDX-License-Identifier: GPL-2.0-or-later */ +#include "sd-json.h" + #include "device-private.h" #include "device-util.h" -#include "format-table.h" #include "help-util.h" #include "log.h" #include "options.h" #include "udev-builtin.h" #include "udevadm.h" #include "udevadm-util.h" +#include "verbs.h" static sd_device_action_t arg_action = SD_DEVICE_ADD; static const char *arg_command = NULL; static const char *arg_syspath = NULL; -static int help(void) { - _cleanup_(table_unrefp) Table *options = NULL; - int r; - - r = option_parser_get_help_table_ns("udevadm-test-builtin", &options); - if (r < 0) - return r; - - help_cmdline("test-builtin [OPTIONS] COMMAND DEVPATH"); - help_abstract("Test a built-in command."); - help_section("Options"); - r = table_print_or_warn(options); - if (r < 0) - return r; - - help_section("Commands"); - udev_builtin_list(); - return 0; -} - static int parse_argv(int argc, char *argv[]) { int r; @@ -48,7 +30,13 @@ static int parse_argv(int argc, char *argv[]) { OPTION_NAMESPACE("udevadm-test-builtin"): {} OPTION_COMMON_HELP: - return help(); + r = command_print_verb_help("test-builtin"); + if (r < 0) + return r; + + help_section("Commands"); + udev_builtin_list(); + return 0; OPTION('V', "version", NULL, "Show package version"): return print_version(); @@ -58,6 +46,9 @@ static int parse_argv(int argc, char *argv[]) { if (r <= 0) return r; break; + + OPTION_COMMON_INTROSPECT_CLI: + return introspect_cli(SD_JSON_FORMAT_OFF); } if (option_parser_get_n_args(&opts) != 2) From 2f15edc2abcd800060181b6ded64488693405ee6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Zbigniew=20J=C4=99drzejewski-Szmek?= Date: Wed, 26 Aug 2026 10:17:06 +0200 Subject: [PATCH 18/21] udevadm-trigger: use common verb help and add --introspect-cli The custom help() is replaced by the common verb help, which derives the synopsis, abstract, and option list from the verb declaration in udevadm.c and the "udevadm-trigger" option namespace. --- src/udev/udevadm-trigger.c | 28 ++++++---------------------- 1 file changed, 6 insertions(+), 22 deletions(-) diff --git a/src/udev/udevadm-trigger.c b/src/udev/udevadm-trigger.c index 5d7752783f..bd1d3267d4 100644 --- a/src/udev/udevadm-trigger.c +++ b/src/udev/udevadm-trigger.c @@ -4,13 +4,12 @@ #include "sd-device.h" #include "sd-event.h" +#include "sd-json.h" #include "alloc-util.h" #include "device-enumerator-private.h" #include "device-private.h" #include "device-util.h" -#include "format-table.h" -#include "help-util.h" #include "id128-util.h" #include "options.h" #include "set.h" @@ -20,6 +19,7 @@ #include "time-util.h" #include "udevadm.h" #include "udevadm-util.h" +#include "verbs.h" #include "virt.h" typedef enum { @@ -321,25 +321,6 @@ static int setup_matches(sd_device_enumerator *e) { return 0; } -static int help(void) { - _cleanup_(table_unrefp) Table *options = NULL; - int r; - - r = option_parser_get_help_table_ns("udevadm-trigger", &options); - if (r < 0) - return r; - - help_cmdline("trigger [OPTIONS] DEVPATH"); - help_abstract("Request events from the kernel."); - help_section("Options"); - r = table_print_or_warn(options); - if (r < 0) - return r; - - help_man_page_reference("udevadm", "8"); - return 0; -} - static int parse_argv(int argc, char *argv[]) { int r; @@ -354,7 +335,7 @@ static int parse_argv(int argc, char *argv[]) { OPTION_NAMESPACE("udevadm-trigger"): {} OPTION_COMMON_HELP: - return help(); + return command_print_verb_help("trigger"); OPTION('V', "version", NULL, "Show package version"): return print_version(); @@ -483,6 +464,9 @@ static int parse_argv(int argc, char *argv[]) { if (r < 0) return log_oom(); break; + + OPTION_COMMON_INTROSPECT_CLI: + return introspect_cli(SD_JSON_FORMAT_OFF); } r = strv_extend_strv(&arg_devices, option_parser_get_args(&opts), /* filter_duplicates= */ false); From 2775c49688eecccd662bc69df733f5872702b334 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Zbigniew=20J=C4=99drzejewski-Szmek?= Date: Wed, 26 Aug 2026 10:17:06 +0200 Subject: [PATCH 19/21] udevadm-verify: use common verb help and add --introspect-cli The custom help() is replaced by the common verb help, which derives the synopsis, abstract, and option list from the verb declaration in udevadm.c and the "udevadm-verify" option namespace. --- src/udev/udevadm-verify.c | 29 +++++++---------------------- 1 file changed, 7 insertions(+), 22 deletions(-) diff --git a/src/udev/udevadm-verify.c b/src/udev/udevadm-verify.c index e7f803dfb1..d850315ca8 100644 --- a/src/udev/udevadm-verify.c +++ b/src/udev/udevadm-verify.c @@ -2,12 +2,12 @@ #include +#include "sd-json.h" + #include "alloc-util.h" #include "ansi-color.h" #include "conf-files.h" #include "errno-util.h" -#include "format-table.h" -#include "help-util.h" #include "log.h" #include "options.h" #include "parse-argument.h" @@ -15,6 +15,7 @@ #include "udev-rules.h" #include "udevadm.h" #include "udevadm-util.h" +#include "verbs.h" static ResolveNameTiming arg_resolve_name_timing = RESOLVE_NAME_EARLY; static char *arg_root = NULL; @@ -23,25 +24,6 @@ static bool arg_style = true; STATIC_DESTRUCTOR_REGISTER(arg_root, freep); -static int help(void) { - _cleanup_(table_unrefp) Table *options = NULL; - int r; - - r = option_parser_get_help_table_ns("udevadm-verify", &options); - if (r < 0) - return r; - - help_cmdline("verify [OPTIONS] [FILE...]"); - help_abstract("Verify udev rules files."); - help_section("Options"); - r = table_print_or_warn(options); - if (r < 0) - return r; - - help_man_page_reference("udevadm", "8"); - return 0; -} - static int parse_argv(int argc, char *argv[], char ***remaining_args) { int r; @@ -57,7 +39,7 @@ static int parse_argv(int argc, char *argv[], char ***remaining_args) { OPTION_NAMESPACE("udevadm-verify"): {} OPTION_COMMON_HELP: - return help(); + return command_print_verb_help("verify"); OPTION('V', "version", NULL, "Show package version"): return print_version(); @@ -81,6 +63,9 @@ static int parse_argv(int argc, char *argv[], char ***remaining_args) { OPTION_LONG("no-style", NULL, "Ignore style issues"): arg_style = false; break; + + OPTION_COMMON_INTROSPECT_CLI: + return introspect_cli(SD_JSON_FORMAT_OFF); } *remaining_args = option_parser_get_args(&opts); From b1a68327bbfb14b868944c9fa324aa9adc6fe10b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Zbigniew=20J=C4=99drzejewski-Szmek?= Date: Wed, 26 Aug 2026 10:17:06 +0200 Subject: [PATCH 20/21] udevadm-wait: use common verb help and add --introspect-cli The custom help() is replaced by the common verb help, which derives the synopsis, abstract, and option list from the verb declaration in udevadm.c and the "udevadm-wait" option namespace. The verb's one-line description from the abstract is retained, I think it is good enough but shorter. --- src/udev/udevadm-wait.c | 28 ++++++---------------------- 1 file changed, 6 insertions(+), 22 deletions(-) diff --git a/src/udev/udevadm-wait.c b/src/udev/udevadm-wait.c index 08c142d5eb..cdee4c8c58 100644 --- a/src/udev/udevadm-wait.c +++ b/src/udev/udevadm-wait.c @@ -4,13 +4,12 @@ #include #include "sd-event.h" +#include "sd-json.h" #include "device-monitor-private.h" #include "device-util.h" #include "event-util.h" -#include "format-table.h" #include "fs-util.h" -#include "help-util.h" #include "options.h" #include "parse-util.h" #include "path-util.h" @@ -21,6 +20,7 @@ #include "time-util.h" #include "udev-util.h" #include "udevadm.h" +#include "verbs.h" typedef enum WaitUntil { WAIT_UNTIL_INITIALIZED, @@ -298,25 +298,6 @@ static int setup_periodic_timer(sd_event *event) { return sd_event_source_set_floating(s, true); } -static int help(void) { - _cleanup_(table_unrefp) Table *options = NULL; - int r; - - r = option_parser_get_help_table_ns("udevadm-wait", &options); - if (r < 0) - return r; - - help_cmdline("wait [OPTIONS] DEVICE [DEVICE…]"); - help_abstract("Wait for devices or device symlinks being created."); - help_section("Options"); - r = table_print_or_warn(options); - if (r < 0) - return r; - - help_man_page_reference("udevadm", "8"); - return 0; -} - static int parse_argv(int argc, char *argv[]) { int r; @@ -331,7 +312,7 @@ static int parse_argv(int argc, char *argv[]) { OPTION_NAMESPACE("udevadm-wait"): {} OPTION_COMMON_HELP: - return help(); + return command_print_verb_help("wait"); OPTION('V', "version", NULL, "Show package version"): return print_version(); @@ -357,6 +338,9 @@ static int parse_argv(int argc, char *argv[]) { OPTION_LONG("settle", NULL, "Also wait for all queued events being processed"): arg_settle = true; break; + + OPTION_COMMON_INTROSPECT_CLI: + return introspect_cli(SD_JSON_FORMAT_OFF); } if (arg_removed) From 378e6c544b9e9c99bd02a9466500184bf68051ba Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Zbigniew=20J=C4=99drzejewski-Szmek?= Date: Wed, 26 Aug 2026 10:18:16 +0200 Subject: [PATCH 21/21] udevd: convert to VERB_COMMAND and add --introspect-cli MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit systemd-udevd is the same binary as udevadm, so the introspection data describes both commands, from either name. The COMMAND intentionally owns no verbs — all of udevadm's verbs are defined in udevadm.c right after its own COMMAND, so that they are attributed to it. The -V/--version output (GIT_VERSION) is preserved. --- src/udev/udev-config.c | 39 ++++++++----------- .../units/TEST-74-AUX-UTILS.introspect-cli.sh | 17 ++++++++ 2 files changed, 33 insertions(+), 23 deletions(-) diff --git a/src/udev/udev-config.c b/src/udev/udev-config.c index c108985c4d..0733f41872 100644 --- a/src/udev/udev-config.c +++ b/src/udev/udev-config.c @@ -2,14 +2,14 @@ #include +#include "sd-json.h" + #include "conf-parser.h" #include "cpu-set-util.h" #include "daemon-util.h" #include "fd-util.h" #include "fileio.h" -#include "format-table.h" #include "hashmap.h" -#include "help-util.h" #include "limits-util.h" #include "options.h" #include "parse-util.h" @@ -25,6 +25,7 @@ #include "udev-rules.h" #include "udev-util.h" #include "udev-worker.h" +#include "verbs.h" #include "version.h" #define WORKER_NUM_MAX UINT64_C(2048) @@ -149,26 +150,15 @@ static int parse_proc_cmdline_item(const char *key, const char *value, void *dat return 0; } -static int help(void) { - _cleanup_(table_unrefp) Table *options = NULL; - int r; - - r = option_parser_get_help_table_ns("udevd", &options); - if (r < 0) - return r; - - help_cmdline("[OPTIONS...]"); - help_abstract("Rule-based manager for device events and files."); - - help_section("Options"); - - r = table_print_or_warn(options); - if (r < 0) - return r; - - help_man_page_reference("systemd-udevd.service", "8"); - return 0; -} +/* systemd-udevd is the same binary as udevadm. This COMMAND intentionally owns no verbs: all of + * udevadm's verbs are defined in udevadm.c right after its own COMMAND, so that they are attributed + * to it. */ +COMMAND( + "systemd-udevd\0", + "Rule-based manager for device events and files.", + .man_pages = "systemd-udevd.service(8)\0", + .option_namespace = "udevd", +); static int parse_argv(int argc, char *argv[], UdevConfig *config) { int r; @@ -185,7 +175,7 @@ static int parse_argv(int argc, char *argv[], UdevConfig *config) { OPTION_NAMESPACE("udevd"): {} OPTION_COMMON_HELP: - return help(); + return command_print_help_name("systemd-udevd"); OPTION('V', "version", NULL, "Show package version"): printf("%s\n", GIT_VERSION); @@ -234,6 +224,9 @@ static int parse_argv(int argc, char *argv[], UdevConfig *config) { else config->timeout_signal = r; break; + + OPTION_COMMON_INTROSPECT_CLI: + return introspect_cli(SD_JSON_FORMAT_OFF); } return 1; diff --git a/test/units/TEST-74-AUX-UTILS.introspect-cli.sh b/test/units/TEST-74-AUX-UTILS.introspect-cli.sh index 40116bd870..8c6e0e0ad3 100755 --- a/test/units/TEST-74-AUX-UTILS.introspect-cli.sh +++ b/test/units/TEST-74-AUX-UTILS.introspect-cli.sh @@ -136,6 +136,7 @@ INTROSPECTABLE=( systemd-tpm2-clear systemd-tpm2-setup systemd-tty-ask-password-agent + systemd-udevd systemd-umount systemd-update-done systemd-validatefs @@ -222,6 +223,22 @@ udevadm --introspect-cli | jq -e \ udevadm lock -h | grep -E '^> udevadm .* lock .* COMMAND$' udevadm lock -h | grep -E '^> udevadm .* lock .* --print$' +# udevadm is also systemd-udevd +udevadm --introspect-cli | jq -e \ + '[.commands[].names[0]] | sort == ["systemd-udevd", "udevadm"]' + +# The systemd-udevd command must own no verbs: the help-verb check in the loop above relies on +# this to never run 'systemd-udevd help', which would start the daemon. +udevadm --introspect-cli | jq -e \ + '.commands[] | select(.names[0] == "systemd-udevd") | (.verbs // []) == []' + +# Each subcommand's help and introspection must work +for v in cat control hwdb info lock monitor settle test test-builtin trigger verify wait; do + udevadm "$v" --help >/dev/null + udevadm "$v" --introspect-cli | jq -e \ + '.mediaType == "application/vnd.io.systemd.cli-introspection-0"' +done + # systemd is optionally a multicall binary that also provides systemd-executor if [[ "$(readlink "$(command -v systemd-executor)" 2>/dev/null)" == systemd ]]; then systemd --introspect-cli | jq -e \