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] 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 \