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.
This commit is contained in:
Zbigniew Jędrzejewski-Szmek
2026-08-28 09:29:04 +02:00
parent 868d22628c
commit a35f5d5eba
2 changed files with 36 additions and 45 deletions
+30 -45
View File
@@ -2,10 +2,10 @@
#include <stdio.h>
#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-<verb>" 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);
@@ -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 \