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.
This commit is contained in:
Zbigniew Jędrzejewski-Szmek
2026-08-28 09:29:04 +02:00
parent 23e8e4e80f
commit 057fc4251a
3 changed files with 73 additions and 13 deletions
+31 -1
View File
@@ -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);