From 494701162fac1b2393599c8cf5e296297ed92941 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Zbigniew=20J=C4=99drzejewski-Szmek?= Date: Sun, 16 Aug 2026 16:52:16 +0200 Subject: [PATCH 01/24] TEST-74-AUX-UTILS.introspect-cli: verify that --help works This is redundant for public programs, for which we test --help more extensively, but is also good as a basic check that e.g. the program name used to print help matches what is specified in COMMAND. --- test/units/TEST-74-AUX-UTILS.introspect-cli.sh | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/test/units/TEST-74-AUX-UTILS.introspect-cli.sh b/test/units/TEST-74-AUX-UTILS.introspect-cli.sh index f7a880859c..9c094de882 100755 --- a/test/units/TEST-74-AUX-UTILS.introspect-cli.sh +++ b/test/units/TEST-74-AUX-UTILS.introspect-cli.sh @@ -7,6 +7,8 @@ set -o pipefail # Check the other location too. PATH=$PATH:/usr/lib/systemd +export SYSTEMD_PAGER=cat + # A smoke test for the introspection code INTROSPECTABLE=( homectl @@ -45,6 +47,8 @@ INTROSPECTABLE=( for i in "${INTROSPECTABLE[@]}"; do command -v "$i" >/dev/null || continue + $i --help >/dev/null + $i --introspect-cli | jq -e \ '.mediaType == "application/vnd.io.systemd.cli-introspection-0"' $i --introspect-cli | jq -e --arg name "$i" \ @@ -57,10 +61,10 @@ done # systemd-hwdb defines verbs, check that they are described if command -v systemd-hwdb >/dev/null; then systemd-hwdb --introspect-cli | jq -e \ - '.commands[0].verbs | map(.names[0]) | contains(["query", "update"])' + '.commands[0].verbs | map(.names[0]) | sort == ["help", "query", "update"]' fi -# systemd-id128 defines verbs, check that they are described +# systemd-id128 defines many verbs, check that some are described systemd-id128 --introspect-cli | jq -e \ '.commands[0].verbs | map(.names[0]) | contains(["new", "machine-id", "show", "help"])' From ec964d79cba7d2c3bf1a248d615e9f6564200555 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Zbigniew=20J=C4=99drzejewski-Szmek?= Date: Sun, 16 Aug 2026 13:03:45 +0200 Subject: [PATCH 02/24] boot-check-no-failures: convert to VERB_COMMAND and add --introspect-cli The custom help() is replaced by a COMMAND() description, and --introspect-cli is added. --- src/bless-boot/boot-check-no-failures.c | 44 +++++-------------- .../units/TEST-74-AUX-UTILS.introspect-cli.sh | 1 + 2 files changed, 12 insertions(+), 33 deletions(-) diff --git a/src/bless-boot/boot-check-no-failures.c b/src/bless-boot/boot-check-no-failures.c index 9fa42a7ed6..a54bd62096 100644 --- a/src/bless-boot/boot-check-no-failures.c +++ b/src/bless-boot/boot-check-no-failures.c @@ -1,44 +1,19 @@ /* SPDX-License-Identifier: LGPL-2.1-or-later */ -#include - #include "sd-bus.h" +#include "sd-json.h" -#include "alloc-util.h" #include "build.h" #include "bus-error.h" -#include "format-table.h" #include "log.h" #include "main-func.h" -#include "options.h" -#include "pretty-print.h" +#include "verbs.h" -static int help(void) { - _cleanup_free_ char *link = NULL; - _cleanup_(table_unrefp) Table *options = NULL; - int r; - - r = terminal_urlify_man("systemd-boot-check-no-failures.service", "8", &link); - if (r < 0) - return log_oom(); - - r = option_parser_get_help_table(&options); - if (r < 0) - return r; - - printf("%s [OPTIONS...]\n" - "\n%sVerify system operational state.%s\n\n", - program_invocation_short_name, - ansi_highlight(), - ansi_normal()); - - r = table_print_or_warn(options); - if (r < 0) - return r; - - printf("\nSee the %s for details.\n", link); - return 0; -} +COMMAND( + "systemd-boot-check-no-failures\0", + "Verify system operational state.", + .man_pages = "systemd-boot-check-no-failures.service.8\0", +); static int parse_argv(int argc, char *argv[]) { assert(argc >= 0); @@ -50,10 +25,13 @@ static int parse_argv(int argc, char *argv[]) { switch (c) { OPTION_COMMON_HELP: - return help(); + return command_print_help("systemd-boot-check-no-failures"); OPTION_COMMON_VERSION: return version(); + + 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 9c094de882..90d01b1779 100755 --- a/test/units/TEST-74-AUX-UTILS.introspect-cli.sh +++ b/test/units/TEST-74-AUX-UTILS.introspect-cli.sh @@ -19,6 +19,7 @@ INTROSPECTABLE=( systemd-backlight systemd-battery-check systemd-binfmt + systemd-boot-check-no-failures systemd-bsod systemd-cat systemd-cgls From fca21f54d73b7d50881a464556d21936c05d3800 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Zbigniew=20J=C4=99drzejewski-Szmek?= Date: Sun, 16 Aug 2026 13:04:47 +0200 Subject: [PATCH 03/24] clonesetup: convert to VERB_COMMAND and add --introspect-cli The custom help() is replaced by a COMMAND() description, and --introspect-cli is added. The two hand-written synopsis lines are replaced by the generic one, and --help now lists the verbs in a Commands: section generated from the verb table. --- src/clonesetup/clonesetup.c | 36 +++++++------------ .../units/TEST-74-AUX-UTILS.introspect-cli.sh | 1 + 2 files changed, 13 insertions(+), 24 deletions(-) diff --git a/src/clonesetup/clonesetup.c b/src/clonesetup/clonesetup.c index debd10590a..569aa4f245 100644 --- a/src/clonesetup/clonesetup.c +++ b/src/clonesetup/clonesetup.c @@ -1,16 +1,15 @@ /* SPDX-License-Identifier: LGPL-2.1-or-later */ #include +#include "sd-json.h" + #include "alloc-util.h" #include "build.h" #include "clonesetup-ioctl.h" #include "clonesetup-util.h" #include "extract-word.h" -#include "format-table.h" -#include "help-util.h" #include "log.h" #include "main-func.h" -#include "options.h" #include "parse-util.h" #include "string-util.h" #include "strv.h" /* strv_skip */ @@ -22,6 +21,12 @@ #define CLONE_REGION_SIZE_MIN_BYTES (UINT64_C(1) << 12) /* 4 KiB */ #define CLONE_REGION_SIZE_MAX_BYTES (UINT64_C(1) << 30) /* 1 GiB */ +COMMAND( + "systemd-clonesetup\0", + "Add or remove a dm-clone device.", + .man_pages = "systemd-clonesetup.8\0", +); + static int parse_clone_options(const char *options, uint64_t *ret_region_size_bytes) { uint64_t region_size_bytes = CLONE_REGION_SIZE_DEFAULT_BYTES; @@ -136,26 +141,6 @@ static int verb_remove(int argc, char *argv[], uintptr_t data, void *userdata) { return 0; } -static int help(void) { - _cleanup_(table_unrefp) Table *options = NULL; - int r; - - r = option_parser_get_help_table(&options); - if (r < 0) - return r; - - help_cmdline("add NAME SOURCE DEST METADATA [OPTIONS]"); - help_cmdline("remove NAME"); - help_abstract("Add or remove a dm-clone device."); - help_section("Options"); - r = table_print_or_warn(options); - if (r < 0) - return r; - - help_man_page_reference("systemd-clonesetup", "8"); - return 0; -} - static int parse_argv(int argc, char *argv[]) { assert(argc >= 0); assert(argv); @@ -165,10 +150,13 @@ static int parse_argv(int argc, char *argv[]) { switch (c) { OPTION_COMMON_HELP: - return help(); + return command_print_help("systemd-clonesetup"); OPTION_COMMON_VERSION: return version(); + + 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 90d01b1779..e9e8de79b8 100755 --- a/test/units/TEST-74-AUX-UTILS.introspect-cli.sh +++ b/test/units/TEST-74-AUX-UTILS.introspect-cli.sh @@ -24,6 +24,7 @@ INTROSPECTABLE=( systemd-cat systemd-cgls systemd-cgtop + systemd-clonesetup systemd-delta systemd-detect-virt systemd-dissect From 0e313315bfc940c0cb96963bfa9a66915d5386da Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Zbigniew=20J=C4=99drzejewski-Szmek?= Date: Sun, 16 Aug 2026 13:05:11 +0200 Subject: [PATCH 04/24] growfs: convert to VERB_COMMAND and add --introspect-cli The custom help() is replaced by a COMMAND() description, and --introspect-cli is added. The positional argument is spelled MOUNTPOINT in the synopsis, like the other metavars, instead of /path/to/mountpoint. The man page is adjusted to match. --- man/systemd-makefs@.service.xml | 4 +- src/growfs/growfs.c | 47 ++++++------------- .../units/TEST-74-AUX-UTILS.introspect-cli.sh | 1 + 3 files changed, 17 insertions(+), 35 deletions(-) diff --git a/man/systemd-makefs@.service.xml b/man/systemd-makefs@.service.xml index a084f9d08c..ca5a99d806 100644 --- a/man/systemd-makefs@.service.xml +++ b/man/systemd-makefs@.service.xml @@ -30,8 +30,8 @@ systemd-mkswap@device.service systemd-growfs@mountpoint.service systemd-growfs-root.service - /usr/lib/systemd/systemd-makefs - /usr/lib/systemd/systemd-growfs + /usr/lib/systemd/systemd-makefs FSTYPE DEVICE + /usr/lib/systemd/systemd-growfs MOUNTPOINT diff --git a/src/growfs/growfs.c b/src/growfs/growfs.c index 8b1bb9b759..15077f1bd7 100644 --- a/src/growfs/growfs.c +++ b/src/growfs/growfs.c @@ -3,6 +3,8 @@ #include #include +#include "sd-json.h" + #include "alloc-util.h" #include "blockdev-util.h" #include "btrfs-util.h" @@ -13,19 +15,24 @@ #include "dissect-image.h" #include "dlopen-note.h" #include "fd-util.h" -#include "format-table.h" #include "format-util.h" #include "log.h" #include "main-func.h" #include "mountpoint-util.h" -#include "options.h" -#include "pretty-print.h" #include "resize-fs.h" #include "string-util.h" +#include "verbs.h" static const char *arg_target = NULL; static bool arg_dry_run = false; +COMMAND( + "systemd-growfs\0", + "Grow filesystem or encrypted payload to device size.", + .argspec = "MOUNTPOINT\0", + .man_pages = "systemd-growfs@.service.8\0", +); + #if HAVE_LIBCRYPTSETUP static int resize_crypt_luks_device(dev_t devno, const char *fstype, dev_t main_devno) { _cleanup_free_ char *devpath = NULL, *main_devpath = NULL; @@ -128,35 +135,6 @@ static int maybe_resize_underlying_device( return 0; } -static int help(void) { - _cleanup_free_ char *link = NULL; - _cleanup_(table_unrefp) Table *options = NULL; - int r; - - r = terminal_urlify_man("systemd-growfs@.service", "8", &link); - if (r < 0) - return log_oom(); - - r = option_parser_get_help_table(&options); - if (r < 0) - return r; - - printf("%s [OPTIONS...] /path/to/mountpoint\n\n" - "Grow filesystem or encrypted payload to device size.\n", - program_invocation_short_name); - - printf("\n%sOptions:%s\n", - ansi_underline(), - ansi_normal()); - - r = table_print_or_warn(options); - if (r < 0) - return r; - - printf("\nSee the %s for details.\n", link); - return 0; -} - static int parse_argv(int argc, char *argv[]) { assert(argc >= 0); assert(argv); @@ -167,7 +145,7 @@ static int parse_argv(int argc, char *argv[]) { switch (c) { OPTION_COMMON_HELP: - return help(); + return command_print_help("systemd-growfs"); OPTION_COMMON_VERSION: return version(); @@ -175,6 +153,9 @@ static int parse_argv(int argc, char *argv[]) { OPTION('n', "dry-run", NULL, "Just print what would be done"): arg_dry_run = true; break; + + OPTION_COMMON_INTROSPECT_CLI: + return introspect_cli(SD_JSON_FORMAT_OFF); } if (option_parser_get_n_args(&opts) != 1) diff --git a/test/units/TEST-74-AUX-UTILS.introspect-cli.sh b/test/units/TEST-74-AUX-UTILS.introspect-cli.sh index e9e8de79b8..8a3c2614c5 100755 --- a/test/units/TEST-74-AUX-UTILS.introspect-cli.sh +++ b/test/units/TEST-74-AUX-UTILS.introspect-cli.sh @@ -30,6 +30,7 @@ INTROSPECTABLE=( systemd-dissect systemd-escape systemd-firstboot + systemd-growfs systemd-hwdb systemd-id128 systemd-imds From 031ff861a6bfdfcc7f1a73ad1bca7f221e87abc0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Zbigniew=20J=C4=99drzejewski-Szmek?= Date: Sun, 16 Aug 2026 14:35:17 +0200 Subject: [PATCH 05/24] hibernate-resume: convert to VERB_COMMAND and add --introspect-cli The custom help() is replaced by a COMMAND() description, and --introspect-cli is added. --clear, which takes no positional arguments, is listed as a separate synopsis line. --- src/hibernate-resume/hibernate-resume.c | 46 ++++++------------- .../units/TEST-74-AUX-UTILS.introspect-cli.sh | 1 + 2 files changed, 16 insertions(+), 31 deletions(-) diff --git a/src/hibernate-resume/hibernate-resume.c b/src/hibernate-resume/hibernate-resume.c index d2dccd59bd..fa7f9bdbaa 100644 --- a/src/hibernate-resume/hibernate-resume.c +++ b/src/hibernate-resume/hibernate-resume.c @@ -2,53 +2,34 @@ #include -#include "alloc-util.h" +#include "sd-json.h" + #include "build.h" #include "devnum-util.h" -#include "format-table.h" #include "hibernate-resume-config.h" #include "hibernate-util.h" #include "initrd-util.h" #include "log.h" #include "main-func.h" -#include "options.h" #include "parse-util.h" -#include "pretty-print.h" #include "stat-util.h" #include "static-destruct.h" #include "strv.h" +#include "verbs.h" static HibernateInfo arg_info = {}; static bool arg_clear = false; STATIC_DESTRUCTOR_REGISTER(arg_info, hibernate_info_done); -static int help(void) { - _cleanup_free_ char *link = NULL; - _cleanup_(table_unrefp) Table *options = NULL; - int r; - - r = terminal_urlify_man("systemd-hibernate-resume", "8", &link); - if (r < 0) - return log_oom(); - - r = option_parser_get_help_table(&options); - if (r < 0) - return r; - - printf("%s [OPTIONS...] [DEVICE [OFFSET]]\n\n" - "%sInitiate resume from hibernation.%s\n\n", - program_invocation_short_name, - ansi_highlight(), - ansi_normal()); - - r = table_print_or_warn(options); - if (r < 0) - return r; - - printf("\nSee the %s for details.\n", link); - return 0; -} +COMMAND( + "systemd-hibernate-resume\0", + "Initiate resume from hibernation.", + .argspec = + "[DEVICE [OFFSET]]\0" + "--clear\0", + .man_pages = "systemd-hibernate-resume.8\0", +); static int parse_argv(int argc, char *argv[], char ***ret_args) { assert(argc >= 0); @@ -61,7 +42,7 @@ static int parse_argv(int argc, char *argv[], char ***ret_args) { switch (c) { OPTION_COMMON_HELP: - return help(); + return command_print_help("systemd-hibernate-resume"); OPTION_COMMON_VERSION: return version(); @@ -70,6 +51,9 @@ static int parse_argv(int argc, char *argv[], char ***ret_args) { "Clear hibernation storage information from EFI and exit"): arg_clear = true; break; + + OPTION_COMMON_INTROSPECT_CLI: + return introspect_cli(SD_JSON_FORMAT_OFF); } if (option_parser_get_n_args(&opts) > 0 && arg_clear) diff --git a/test/units/TEST-74-AUX-UTILS.introspect-cli.sh b/test/units/TEST-74-AUX-UTILS.introspect-cli.sh index 8a3c2614c5..7d4ca2abce 100755 --- a/test/units/TEST-74-AUX-UTILS.introspect-cli.sh +++ b/test/units/TEST-74-AUX-UTILS.introspect-cli.sh @@ -31,6 +31,7 @@ INTROSPECTABLE=( systemd-escape systemd-firstboot systemd-growfs + systemd-hibernate-resume systemd-hwdb systemd-id128 systemd-imds From 6e64a169bd583fa420384d8d3accf376324578e7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Zbigniew=20J=C4=99drzejewski-Szmek?= Date: Sun, 16 Aug 2026 14:35:41 +0200 Subject: [PATCH 06/24] journal-gatewayd: convert to VERB_COMMAND and add --introspect-cli The custom help() is replaced by a COMMAND() description, and --introspect-cli is added. The abstract is changed from the noun phrase "HTTP server for journal events." to the imperative "Serve journal events over HTTP.", consistent with the other commands, and the meaningless "..." placeholder is dropped from the synopsis (the tool takes no positional arguments). --- src/journal-remote/journal-gatewayd.c | 41 ++++++------------- .../units/TEST-74-AUX-UTILS.introspect-cli.sh | 1 + 2 files changed, 13 insertions(+), 29 deletions(-) diff --git a/src/journal-remote/journal-gatewayd.c b/src/journal-remote/journal-gatewayd.c index f2fd083c8a..e135bf1c9b 100644 --- a/src/journal-remote/journal-gatewayd.c +++ b/src/journal-remote/journal-gatewayd.c @@ -9,6 +9,7 @@ #include "sd-bus.h" #include "sd-daemon.h" #include "sd-journal.h" +#include "sd-json.h" #include "alloc-util.h" #include "build.h" @@ -16,7 +17,6 @@ #include "errno-util.h" #include "fd-util.h" #include "fileio.h" -#include "format-table.h" #include "glob-util.h" #include "hostname-setup.h" #include "hostname-util.h" @@ -27,15 +27,14 @@ #include "main-func.h" #include "memory-util.h" #include "microhttpd-util.h" -#include "options.h" #include "os-util.h" #include "output-mode.h" #include "parse-util.h" -#include "pretty-print.h" #include "signal-util.h" #include "string-util.h" #include "time-util.h" #include "tmpfile-util.h" +#include "verbs.h" #define JOURNAL_WAIT_TIMEOUT (10*USEC_PER_SEC) @@ -53,6 +52,12 @@ STATIC_DESTRUCTOR_REGISTER(arg_trust_pem, freep); STATIC_DESTRUCTOR_REGISTER(arg_directory, freep); STATIC_DESTRUCTOR_REGISTER(arg_file, strv_freep); +COMMAND( + "systemd-journal-gatewayd\0", + "Serve journal events over HTTP.", + .man_pages = "systemd-journal-gatewayd.service.8\0", +); + typedef struct RequestMeta { sd_journal *journal; @@ -1096,31 +1101,6 @@ static mhd_result request_handler( return mhd_respond(connection, MHD_HTTP_NOT_FOUND, "Not found."); } -static int help(void) { - _cleanup_free_ char *link = NULL; - _cleanup_(table_unrefp) Table *options = NULL; - int r; - - r = terminal_urlify_man("systemd-journal-gatewayd.service", "8", &link); - if (r < 0) - return log_oom(); - - r = option_parser_get_help_table(&options); - if (r < 0) - return r; - - printf("%s [OPTIONS...] ...\n\n" - "HTTP server for journal events.\n\n", - program_invocation_short_name); - - r = table_print_or_warn(options); - if (r < 0) - return r; - - printf("\nSee the %s for details.\n", link); - return 0; -} - static int parse_argv(int argc, char *argv[]) { assert(argc >= 0); assert(argv); @@ -1132,7 +1112,7 @@ static int parse_argv(int argc, char *argv[]) { switch (c) { OPTION_COMMON_HELP: - return help(); + return command_print_help("systemd-journal-gatewayd"); OPTION_COMMON_VERSION: return version(); @@ -1207,6 +1187,9 @@ static int parse_argv(int argc, char *argv[]) { if (r < 0) return log_error_errno(r, "Failed to add paths: %m"); break; + + OPTION_COMMON_INTROSPECT_CLI: + return introspect_cli(SD_JSON_FORMAT_OFF); } if (option_parser_get_n_args(&opts) > 0) diff --git a/test/units/TEST-74-AUX-UTILS.introspect-cli.sh b/test/units/TEST-74-AUX-UTILS.introspect-cli.sh index 7d4ca2abce..ca120a7b27 100755 --- a/test/units/TEST-74-AUX-UTILS.introspect-cli.sh +++ b/test/units/TEST-74-AUX-UTILS.introspect-cli.sh @@ -36,6 +36,7 @@ INTROSPECTABLE=( systemd-id128 systemd-imds systemd-inhibit + systemd-journal-gatewayd systemd-mute-console systemd-notify systemd-oomd From 9cea0873a06a1bb7123af5b63087d6de78f71a65 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Zbigniew=20J=C4=99drzejewski-Szmek?= Date: Sun, 16 Aug 2026 14:36:03 +0200 Subject: [PATCH 07/24] journal-remote: convert to VERB_COMMAND and add --introspect-cli The custom help() is replaced by a COMMAND() description, and --introspect-cli is added. The synopsis now marks the input files as optional, since input may also come from --url=, --getter=, --listen-*= or passed file descriptors, matching the man page. The note about sd_listen_fds() is kept as the footer. --- src/journal-remote/journal-remote-main.c | 53 ++++++------------- .../units/TEST-74-AUX-UTILS.introspect-cli.sh | 1 + 2 files changed, 16 insertions(+), 38 deletions(-) diff --git a/src/journal-remote/journal-remote-main.c b/src/journal-remote/journal-remote-main.c index 30757201f3..09f010728d 100644 --- a/src/journal-remote/journal-remote-main.c +++ b/src/journal-remote/journal-remote-main.c @@ -4,6 +4,7 @@ #include "sd-daemon.h" #include "sd-event.h" +#include "sd-json.h" #include "alloc-util.h" #include "build.h" @@ -12,9 +13,8 @@ #include "dlopen-note.h" #include "extract-word.h" #include "fd-util.h" -#include "format-table.h" -#include "format-util.h" #include "fileio.h" +#include "format-util.h" #include "hashmap.h" #include "journal-authenticate.h" #include "journal-compression-util.h" @@ -23,12 +23,10 @@ #include "logs-show.h" #include "main-func.h" #include "microhttpd-util.h" -#include "options.h" #include "parse-argument.h" #include "parse-helpers.h" #include "parse-util.h" #include "path-util.h" -#include "pretty-print.h" #include "process-util.h" #include "socket-netlink.h" #include "socket-util.h" @@ -36,6 +34,7 @@ #include "string-table.h" #include "string-util.h" #include "strv.h" +#include "verbs.h" #define PRIV_KEY_FILE CERTIFICATE_ROOT "/private/journal-remote.pem" #define CERT_FILE CERTIFICATE_ROOT "/certs/journal-remote.pem" @@ -84,6 +83,14 @@ STATIC_DESTRUCTOR_REGISTER(arg_trust, freep); STATIC_DESTRUCTOR_REGISTER(arg_output, freep); STATIC_DESTRUCTOR_REGISTER(arg_compression, ordered_hashmap_freep); +COMMAND( + "systemd-journal-remote\0", + "Write external journal events to journal files.", + .argspec = "[{FILE|-}…]\0", + .footer = "Note: file descriptors from sd_listen_fds() will be consumed, too.", + .man_pages = "systemd-journal-remote.service.8\0", +); + static const char* const journal_write_split_mode_table[_JOURNAL_WRITE_SPLIT_MAX] = { [JOURNAL_WRITE_SPLIT_NONE] = "none", [JOURNAL_WRITE_SPLIT_HOST] = "host", @@ -869,39 +876,6 @@ static int parse_config(void) { /* userdata= */ NULL); } -static int help(void) { - _cleanup_free_ char *link = NULL; - _cleanup_(table_unrefp) Table *options = NULL; - int r; - - r = terminal_urlify_man("systemd-journal-remote.service", "8", &link); - if (r < 0) - return log_oom(); - - r = option_parser_get_help_table(&options); - if (r < 0) - return r; - - printf("%s [OPTIONS...] {FILE|-}...\n" - "\n%sWrite external journal events to journal file(s).%s\n" - "\n%sOptions:%s\n", - program_invocation_short_name, - ansi_highlight(), - ansi_normal(), - ansi_underline(), - ansi_normal()); - - r = table_print_or_warn(options); - if (r < 0) - return r; - - printf("\nNote: file descriptors from sd_listen_fds() will be consumed, too.\n" - "\nSee the %s for details.\n", - link); - - return 0; -} - static int parse_argv(int argc, char *argv[]) { int r; bool type_a, type_b; @@ -915,7 +889,7 @@ static int parse_argv(int argc, char *argv[]) { switch (c) { OPTION_COMMON_HELP: - return help(); + return command_print_help("systemd-journal-remote"); OPTION_COMMON_VERSION: return version(); @@ -1059,6 +1033,9 @@ static int parse_argv(int argc, char *argv[]) { if (r < 0) return log_error_errno(r, "Failed to parse --max-files= value: %s", opts.arg); break; + + OPTION_COMMON_INTROSPECT_CLI: + return introspect_cli(SD_JSON_FORMAT_OFF); } arg_files = strv_copy(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 ca120a7b27..cbeadc2317 100755 --- a/test/units/TEST-74-AUX-UTILS.introspect-cli.sh +++ b/test/units/TEST-74-AUX-UTILS.introspect-cli.sh @@ -37,6 +37,7 @@ INTROSPECTABLE=( systemd-imds systemd-inhibit systemd-journal-gatewayd + systemd-journal-remote systemd-mute-console systemd-notify systemd-oomd From 7125bf3ac458974c895db84e8de0383a3fa96ac4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Zbigniew=20J=C4=99drzejewski-Szmek?= Date: Sun, 16 Aug 2026 14:36:24 +0200 Subject: [PATCH 08/24] journal-upload: convert to VERB_COMMAND and add --introspect-cli The custom help() is replaced by a COMMAND() description, and --introspect-cli is added. The synopsis now marks the input files as optional, since the local journal is read when none are given, and --url= as optional, since URL= may come from journal-upload.conf, matching the man page. --- src/journal-remote/journal-upload.c | 37 +++++++------------ .../units/TEST-74-AUX-UTILS.introspect-cli.sh | 1 + 2 files changed, 14 insertions(+), 24 deletions(-) diff --git a/src/journal-remote/journal-upload.c b/src/journal-remote/journal-upload.c index d680a4c02b..2516d3fa6c 100644 --- a/src/journal-remote/journal-upload.c +++ b/src/journal-remote/journal-upload.c @@ -6,6 +6,7 @@ #include "sd-daemon.h" #include "sd-event.h" +#include "sd-json.h" #include "alloc-util.h" #include "build.h" @@ -17,12 +18,10 @@ #include "extract-word.h" #include "fd-util.h" #include "fileio.h" -#include "format-table.h" #include "format-util.h" #include "fs-util.h" #include "glob-util.h" #include "hashmap.h" -#include "help-util.h" #include "journal-header-util.h" #include "journal-upload.h" #include "journal-util.h" @@ -30,7 +29,6 @@ #include "logs-show.h" #include "main-func.h" #include "mkdir.h" -#include "options.h" #include "parse-argument.h" #include "parse-helpers.h" #include "process-util.h" @@ -38,6 +36,7 @@ #include "strv.h" #include "time-util.h" #include "tmpfile-util.h" +#include "verbs.h" #include "version.h" #define PRIV_KEY_FILE CERTIFICATE_ROOT "/private/journal-upload.pem" @@ -78,6 +77,13 @@ STATIC_DESTRUCTOR_REGISTER(arg_save_state, freep); STATIC_DESTRUCTOR_REGISTER(arg_compression, ordered_hashmap_freep); STATIC_DESTRUCTOR_REGISTER(arg_headers, ordered_hashmap_freep); +COMMAND( + "systemd-journal-upload\0", + "Upload journal events to a remote server.", + .argspec = "[-u URL] [{FILE|-}…]\0", + .man_pages = "systemd-journal-upload.service.8\0", +); + static void close_fd_input(Uploader *u); #define SERVER_ANSWER_KEEP 2048 @@ -684,26 +690,6 @@ static int parse_config(void) { /* userdata= */ NULL); } -static int help(void) { - _cleanup_(table_unrefp) Table *options = NULL; - int r; - - r = option_parser_get_help_table(&options); - if (r < 0) - return r; - - help_cmdline("-u URL {FILE|-}..."); - help_abstract("Upload journal events to a remote server."); - - help_section("Options"); - r = table_print_or_warn(options); - if (r < 0) - return r; - - help_man_page_reference("systemd-journal-upload.service", "8"); - return 0; -} - static int parse_argv(int argc, char *argv[], char ***ret_args) { int r; @@ -717,7 +703,7 @@ static int parse_argv(int argc, char *argv[], char ***ret_args) { switch (c) { OPTION_COMMON_HELP: - return help(); + return command_print_help("systemd-journal-upload"); OPTION_COMMON_VERSION: return version(); @@ -824,6 +810,9 @@ static int parse_argv(int argc, char *argv[], char ***ret_args) { if (r < 0) return r; break; + + OPTION_COMMON_INTROSPECT_CLI: + return introspect_cli(SD_JSON_FORMAT_OFF); } if (!arg_url) diff --git a/test/units/TEST-74-AUX-UTILS.introspect-cli.sh b/test/units/TEST-74-AUX-UTILS.introspect-cli.sh index cbeadc2317..55f95fa1a4 100755 --- a/test/units/TEST-74-AUX-UTILS.introspect-cli.sh +++ b/test/units/TEST-74-AUX-UTILS.introspect-cli.sh @@ -38,6 +38,7 @@ INTROSPECTABLE=( systemd-inhibit systemd-journal-gatewayd systemd-journal-remote + systemd-journal-upload systemd-mute-console systemd-notify systemd-oomd From c590744048aad190da6203b71edcffd26935fb78 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Zbigniew=20J=C4=99drzejewski-Szmek?= Date: Sun, 16 Aug 2026 14:36:42 +0200 Subject: [PATCH 09/24] modules-load: convert to VERB_COMMAND 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 a COMMAND() description, and --introspect-cli is added. The abstract is changed to use the imperative form ("Load …" instead of "Loads …"), consistent with the other commands. --- src/modules-load/modules-load.c | 42 +++++++------------ .../units/TEST-74-AUX-UTILS.introspect-cli.sh | 1 + 2 files changed, 15 insertions(+), 28 deletions(-) diff --git a/src/modules-load/modules-load.c b/src/modules-load/modules-load.c index 85452f62a5..9ae6531472 100644 --- a/src/modules-load/modules-load.c +++ b/src/modules-load/modules-load.c @@ -6,6 +6,8 @@ #include #include +#include "sd-json.h" + #include "alloc-util.h" #include "assert.h" #include "build.h" @@ -16,18 +18,16 @@ #include "errno-util.h" #include "fd-util.h" #include "fileio.h" -#include "format-table.h" #include "log.h" #include "macro.h" #include "main-func.h" #include "module-util.h" -#include "options.h" #include "ordered-set.h" #include "parse-util.h" -#include "pretty-print.h" #include "proc-cmdline.h" #include "string-util.h" #include "strv.h" +#include "verbs.h" #define MODULE_NAME_MAX_LEN (4096UL) @@ -36,6 +36,13 @@ static const char conf_file_dirs[] = CONF_PATHS_NULSTR("modules-load.d"); STATIC_DESTRUCTOR_REGISTER(arg_proc_cmdline_modules, strv_freep); +COMMAND( + "systemd-modules-load\0", + "Load statically configured kernel modules.", + .argspec = "[CONFIGURATION FILE…]\0", + .man_pages = "systemd-modules-load.service.8\0", +); + static int modules_list_append_suffix(OrderedSet **module_set, char *modp) { /* take possession of string no matter what */ _cleanup_free_ char *mod = modp; @@ -332,30 +339,6 @@ static unsigned determine_num_worker_threads(unsigned n_modules) { return n_threads - 1U; } -static int help(void) { - _cleanup_free_ char *link = NULL; - _cleanup_(table_unrefp) Table *options = NULL; - int r; - - if (terminal_urlify_man("systemd-modules-load.service", "8", &link) < 0) - return log_oom(); - - r = option_parser_get_help_table(&options); - if (r < 0) - return r; - - printf("%s [OPTIONS...] [CONFIGURATION FILE...]\n\n" - "Loads statically configured kernel modules.\n\n", - program_invocation_short_name); - - r = table_print_or_warn(options); - if (r < 0) - return r; - - printf("\nSee the %s for details.\n", link); - return 0; -} - static int parse_argv(int argc, char *argv[], char ***ret_args) { assert(argc >= 0); assert(argv); @@ -367,10 +350,13 @@ static int parse_argv(int argc, char *argv[], char ***ret_args) { switch (c) { OPTION_COMMON_HELP: - return help(); + return command_print_help("systemd-modules-load"); OPTION_COMMON_VERSION: return version(); + + OPTION_COMMON_INTROSPECT_CLI: + return introspect_cli(SD_JSON_FORMAT_OFF); } *ret_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 55f95fa1a4..dea9fb713d 100755 --- a/test/units/TEST-74-AUX-UTILS.introspect-cli.sh +++ b/test/units/TEST-74-AUX-UTILS.introspect-cli.sh @@ -39,6 +39,7 @@ INTROSPECTABLE=( systemd-journal-gatewayd systemd-journal-remote systemd-journal-upload + systemd-modules-load systemd-mute-console systemd-notify systemd-oomd From 9da7b27037d471a2a03186882df56aeec47c841f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Zbigniew=20J=C4=99drzejewski-Szmek?= Date: Sun, 16 Aug 2026 14:37:09 +0200 Subject: [PATCH 10/24] network-generator: convert to VERB_COMMAND and add --introspect-cli The custom help() is replaced by a COMMAND() description, and --introspect-cli is added. The old help had neither an abstract nor a man page reference; both are added, with the abstract taken from the man page. --- .../generator/network-generator-main.c | 35 ++++++++----------- .../units/TEST-74-AUX-UTILS.introspect-cli.sh | 1 + 2 files changed, 15 insertions(+), 21 deletions(-) diff --git a/src/network/generator/network-generator-main.c b/src/network/generator/network-generator-main.c index ae15dff2b1..fb5c54bd13 100644 --- a/src/network/generator/network-generator-main.c +++ b/src/network/generator/network-generator-main.c @@ -2,29 +2,37 @@ #include +#include "sd-json.h" + #include "alloc-util.h" #include "build.h" #include "creds-util.h" #include "dlopen-note.h" #include "errno-util.h" #include "fd-util.h" -#include "format-table.h" #include "fs-util.h" #include "generator.h" #include "log.h" #include "main-func.h" #include "mkdir.h" #include "network-generator.h" -#include "options.h" #include "path-util.h" #include "proc-cmdline.h" #include "string-util.h" #include "strv.h" +#include "verbs.h" #define NETWORK_UNIT_DIRECTORY "/run/systemd/network/" static const char *arg_root = NULL; +COMMAND( + "systemd-network-generator\0", + "Generate network configuration from the kernel command line.", + .argspec = "[-- KERNEL_CMDLINE]\0", + .man_pages = "systemd-network-generator.service.8\0", +); + static int network_save(Network *network, const char *dest_dir) { _cleanup_(unlink_and_freep) char *temp_path = NULL; _cleanup_fclose_ FILE *f = NULL; @@ -150,24 +158,6 @@ static int context_save(Context *context) { return r; } -static int help(void) { - _cleanup_(table_unrefp) Table *options = NULL; - int r; - - r = option_parser_get_help_table(&options); - if (r < 0) - return r; - - printf("%s [OPTIONS...] [-- KERNEL_CMDLINE]\n\n", - program_invocation_short_name); - - r = table_print_or_warn(options); - if (r < 0) - return r; - - return 0; -} - static int parse_argv(int argc, char *argv[], char ***ret_args) { assert(argc >= 0); assert(argv); @@ -179,7 +169,7 @@ static int parse_argv(int argc, char *argv[], char ***ret_args) { switch (c) { OPTION_COMMON_HELP: - return help(); + return command_print_help("systemd-network-generator"); OPTION_COMMON_VERSION: return version(); @@ -188,6 +178,9 @@ static int parse_argv(int argc, char *argv[], char ***ret_args) { "Operate on an alternate filesystem root"): arg_root = opts.arg; break; + + OPTION_COMMON_INTROSPECT_CLI: + return introspect_cli(SD_JSON_FORMAT_OFF); } *ret_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 dea9fb713d..68391c9fcc 100755 --- a/test/units/TEST-74-AUX-UTILS.introspect-cli.sh +++ b/test/units/TEST-74-AUX-UTILS.introspect-cli.sh @@ -41,6 +41,7 @@ INTROSPECTABLE=( systemd-journal-upload systemd-modules-load systemd-mute-console + systemd-network-generator systemd-notify systemd-oomd systemd-path From 4df232569425a124b076dc397ab594ff5273e9a9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Zbigniew=20J=C4=99drzejewski-Szmek?= Date: Sun, 16 Aug 2026 14:37:29 +0200 Subject: [PATCH 11/24] networkd-wait-online: convert to VERB_COMMAND and add --introspect-cli The custom help() is replaced by a COMMAND() description, and --introspect-cli is added. --- src/network/wait-online/wait-online.c | 34 ++++++------------- .../units/TEST-74-AUX-UTILS.introspect-cli.sh | 1 + 2 files changed, 12 insertions(+), 23 deletions(-) diff --git a/src/network/wait-online/wait-online.c b/src/network/wait-online/wait-online.c index e566f89d02..7f6b315bf2 100644 --- a/src/network/wait-online/wait-online.c +++ b/src/network/wait-online/wait-online.c @@ -3,20 +3,19 @@ #include #include "sd-event.h" +#include "sd-json.h" #include "alloc-util.h" #include "build.h" #include "daemon-util.h" -#include "format-table.h" #include "hashmap.h" -#include "help-util.h" #include "log.h" #include "main-func.h" -#include "options.h" #include "parse-argument.h" #include "socket-util.h" #include "strv.h" #include "time-util.h" +#include "verbs.h" #include "wait-online-manager.h" static bool arg_quiet = false; @@ -31,25 +30,11 @@ static bool arg_requires_dns = false; STATIC_DESTRUCTOR_REGISTER(arg_interfaces, hashmap_freep); STATIC_DESTRUCTOR_REGISTER(arg_ignore, strv_freep); -static int help(void) { - _cleanup_(table_unrefp) Table *options = NULL; - int r; - - r = option_parser_get_help_table(&options); - if (r < 0) - return r; - - help_cmdline("[OPTIONS...]"); - help_abstract("Block until network is configured."); - - help_section("Options"); - r = table_print_or_warn(options); - if (r < 0) - return r; - - help_man_page_reference("systemd-networkd-wait-online.service", "8"); - return 0; -} +COMMAND( + "systemd-networkd-wait-online\0", + "Block until network is configured.", + .man_pages = "systemd-networkd-wait-online.service.8\0", +); static int parse_interface_with_operstate_range(const char *str) { _cleanup_free_ char *ifname = NULL; @@ -107,7 +92,7 @@ static int parse_argv(int argc, char *argv[]) { switch (c) { OPTION_COMMON_HELP: - return help(); + return command_print_help("systemd-networkd-wait-online"); OPTION_COMMON_VERSION: return version(); @@ -160,6 +145,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); } 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 68391c9fcc..9a686e99b8 100755 --- a/test/units/TEST-74-AUX-UTILS.introspect-cli.sh +++ b/test/units/TEST-74-AUX-UTILS.introspect-cli.sh @@ -42,6 +42,7 @@ INTROSPECTABLE=( systemd-modules-load systemd-mute-console systemd-network-generator + systemd-networkd-wait-online systemd-notify systemd-oomd systemd-path From d1c345a079185c7d461ada62693dc7309a469683 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Zbigniew=20J=C4=99drzejewski-Szmek?= Date: Sun, 16 Aug 2026 14:37:49 +0200 Subject: [PATCH 12/24] pcrextend: convert to VERB_COMMAND and add --introspect-cli The custom help() is replaced by a COMMAND() description, and --introspect-cli is added. The man page is adjusted to match. --- man/systemd-pcrphase.service.xml | 6 ++- src/pcrextend/pcrextend.c | 45 +++++++------------ .../units/TEST-74-AUX-UTILS.introspect-cli.sh | 1 + 3 files changed, 23 insertions(+), 29 deletions(-) diff --git a/man/systemd-pcrphase.service.xml b/man/systemd-pcrphase.service.xml index c604a8465a..c75ab4d0ac 100644 --- a/man/systemd-pcrphase.service.xml +++ b/man/systemd-pcrphase.service.xml @@ -46,7 +46,11 @@ systemd-pcrnvdone.service systemd-pcrphase-factory-reset.service systemd-pcrphase-storage-target-mode.service - /usr/lib/systemd/systemd-pcrextend STRING + /usr/lib/systemd/systemd-pcrextend WORD + /usr/lib/systemd/systemd-pcrextend + /usr/lib/systemd/systemd-pcrextend + /usr/lib/systemd/systemd-pcrextend + /usr/lib/systemd/systemd-pcrextend diff --git a/src/pcrextend/pcrextend.c b/src/pcrextend/pcrextend.c index 06cc7dc5a7..0ec2943042 100644 --- a/src/pcrextend/pcrextend.c +++ b/src/pcrextend/pcrextend.c @@ -10,11 +10,8 @@ #include "dlopen-note.h" #include "efi-loader.h" #include "escape.h" -#include "format-table.h" -#include "help-util.h" #include "json-util.h" #include "main-func.h" -#include "options.h" #include "parse-argument.h" #include "pcrextend-util.h" #include "string-table.h" @@ -26,6 +23,7 @@ #include "userdb.h" #include "varlink-io.systemd.PCRExtend.h" #include "varlink-util.h" +#include "verbs.h" static bool arg_graceful = false; static char *arg_tpm2_device = NULL; @@ -45,32 +43,20 @@ STATIC_DESTRUCTOR_REGISTER(arg_file_system, freep); STATIC_DESTRUCTOR_REGISTER(arg_nvpcr_name, freep); STATIC_DESTRUCTOR_REGISTER(arg_login, user_record_unrefp); +COMMAND( + "systemd-pcrextend\0", + "Extend a TPM2 PCR with boot phase, machine ID, file system ID or user record.", + .argspec = + "WORD\0" + "--file-system=PATH\0" + "--machine-id\0" + "--product-id\0" + "--login=UID|USER\0", + .man_pages = "systemd-pcrextend.8\0", +); + #define EXTENSION_STRING_SAFE_LIMIT 1024 -static int help(void) { - _cleanup_(table_unrefp) Table *options = NULL; - int r; - - r = option_parser_get_help_table(&options); - if (r < 0) - return r; - - help_cmdline("[OPTIONS...] WORD"); - help_cmdline("[OPTIONS...] --file-system=PATH"); - help_cmdline("[OPTIONS...] --machine-id"); - help_cmdline("[OPTIONS...] --product-id"); - help_cmdline("[OPTIONS...] --login=UID|USER"); - help_abstract("Extend a TPM2 PCR with boot phase, machine ID, file system ID or user record."); - - help_section("Options"); - r = table_print_or_warn(options); - if (r < 0) - return r; - - help_man_page_reference("systemd-pcrextend", "8"); - return 0; -} - static int parse_argv(int argc, char *argv[], char ***ret_args) { assert(argc >= 0); assert(argv); @@ -82,7 +68,7 @@ static int parse_argv(int argc, char *argv[], char ***ret_args) { switch (c) { OPTION_COMMON_HELP: - return help(); + return command_print_help("systemd-pcrextend"); OPTION_COMMON_VERSION: return version(); @@ -185,6 +171,9 @@ static int parse_argv(int argc, char *argv[], char ***ret_args) { if (arg_event_type < 0) return log_error_errno(arg_event_type, "Failed to parse --event-type= argument: %s", opts.arg); break; + + OPTION_COMMON_INTROSPECT_CLI: + return introspect_cli(SD_JSON_FORMAT_OFF); } if (!!arg_file_system + arg_machine_id + arg_product_id + !!arg_login > 1) diff --git a/test/units/TEST-74-AUX-UTILS.introspect-cli.sh b/test/units/TEST-74-AUX-UTILS.introspect-cli.sh index 9a686e99b8..da9cd62566 100755 --- a/test/units/TEST-74-AUX-UTILS.introspect-cli.sh +++ b/test/units/TEST-74-AUX-UTILS.introspect-cli.sh @@ -46,6 +46,7 @@ INTROSPECTABLE=( systemd-notify systemd-oomd systemd-path + systemd-pcrextend systemd-pty-forward systemd-socket-activate systemd-tty-ask-password-agent From 3b9dd1ea53e79aae20a44fbbc634326d18704932 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Zbigniew=20J=C4=99drzejewski-Szmek?= Date: Sun, 16 Aug 2026 14:38:10 +0200 Subject: [PATCH 13/24] socket-proxyd: convert to VERB_COMMAND and add --introspect-cli The custom help() is replaced by a COMMAND() description, and --introspect-cli is added. The synopsis no longer marks the target as optional, since exactly one HOST:PORT or SOCKET argument is required. The hint about systemd.time(7) is kept as the footer. --- src/socket-proxy/socket-proxyd.c | 53 ++++++------------- .../units/TEST-74-AUX-UTILS.introspect-cli.sh | 1 + 2 files changed, 17 insertions(+), 37 deletions(-) diff --git a/src/socket-proxy/socket-proxyd.c b/src/socket-proxy/socket-proxyd.c index 8be25f176e..aa101e9f65 100644 --- a/src/socket-proxy/socket-proxyd.c +++ b/src/socket-proxy/socket-proxyd.c @@ -6,6 +6,7 @@ #include "sd-daemon.h" #include "sd-event.h" +#include "sd-json.h" #include "sd-resolve.h" #include "alloc-util.h" @@ -14,14 +15,11 @@ #include "errno-util.h" #include "event-util.h" #include "fd-util.h" -#include "format-table.h" #include "in-addr-util.h" #include "io-util.h" #include "log.h" #include "main-func.h" -#include "options.h" #include "parse-util.h" -#include "pretty-print.h" #include "resolve-private.h" #include "set.h" #include "socket-forward.h" @@ -30,6 +28,7 @@ #include "string-util.h" #include "strv.h" #include "time-util.h" +#include "verbs.h" static unsigned arg_connections_max = 256; static const char *arg_remote_host = NULL; @@ -50,6 +49,16 @@ static ProxyProtocol arg_proxy_protocol = PROXY_NONE; DEFINE_PRIVATE_STRING_TABLE_LOOKUP_FROM_STRING(proxy_protocol, ProxyProtocol); +COMMAND( + "systemd-socket-proxyd\0", + "Bidirectionally proxy local sockets to another (possibly remote) socket.", + .argspec = + "HOST:PORT\0" + "SOCKET\0", + .footer = "See systemd.time(7) for the --exit-idle-time= time span format.", + .man_pages = "systemd-socket-proxyd.8\0", +); + typedef struct Context { sd_event *event; sd_resolve *resolve; @@ -486,39 +495,6 @@ static int add_listen_socket(Context *context, int fd) { return 0; } -static int help(void) { - _cleanup_free_ char *link = NULL, *time_link = NULL; - _cleanup_(table_unrefp) Table *options = NULL; - int r; - - r = terminal_urlify_man("systemd-socket-proxyd", "8", &link); - if (r < 0) - return log_oom(); - r = terminal_urlify_man("systemd.time", "7", &time_link); - if (r < 0) - return log_oom(); - - r = option_parser_get_help_table(&options); - if (r < 0) - return r; - - printf("%1$s [HOST:PORT]\n" - "%1$s [SOCKET]\n" - "\n%2$sBidirectionally proxy local sockets to another (possibly remote) socket.%3$s\n\n", - program_invocation_short_name, - ansi_highlight(), - ansi_normal()); - - r = table_print_or_warn(options); - if (r < 0) - return r; - - printf("\nSee %s for --exit-idle-time= time span format.\n" - "See the %s for details.\n", - time_link, link); - return 0; -} - static int parse_argv(int argc, char *argv[]) { assert(argc >= 0); assert(argv); @@ -530,7 +506,7 @@ static int parse_argv(int argc, char *argv[]) { switch (c) { OPTION_COMMON_HELP: - return help(); + return command_print_help("systemd-socket-proxyd"); OPTION_COMMON_VERSION: return version(); @@ -560,6 +536,9 @@ static int parse_argv(int argc, char *argv[]) { if (arg_proxy_protocol < 0) return log_error_errno(arg_proxy_protocol, "Failed to parse --proxy-protocol= argument: %s", opts.arg); break; + + OPTION_COMMON_INTROSPECT_CLI: + return introspect_cli(SD_JSON_FORMAT_OFF); } char **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 da9cd62566..af025e4585 100755 --- a/test/units/TEST-74-AUX-UTILS.introspect-cli.sh +++ b/test/units/TEST-74-AUX-UTILS.introspect-cli.sh @@ -49,6 +49,7 @@ INTROSPECTABLE=( systemd-pcrextend systemd-pty-forward systemd-socket-activate + systemd-socket-proxyd systemd-tty-ask-password-agent systemd-veritysetup timedatectl From 67f17eb4c8f92e32bab971a0a66793750a5d8608 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Zbigniew=20J=C4=99drzejewski-Szmek?= Date: Sun, 16 Aug 2026 14:38:30 +0200 Subject: [PATCH 14/24] stdio-bridge: convert to VERB_COMMAND and add --introspect-cli The custom help() is replaced by a COMMAND() description, and --introspect-cli is added. --help now also references the man page, which the old help did not. --- src/stdio-bridge/stdio-bridge.c | 32 +++++++------------ .../units/TEST-74-AUX-UTILS.introspect-cli.sh | 1 + 2 files changed, 12 insertions(+), 21 deletions(-) diff --git a/src/stdio-bridge/stdio-bridge.c b/src/stdio-bridge/stdio-bridge.c index 01686d2cd6..25e6f49a35 100644 --- a/src/stdio-bridge/stdio-bridge.c +++ b/src/stdio-bridge/stdio-bridge.c @@ -5,42 +5,29 @@ #include "sd-bus.h" #include "sd-daemon.h" +#include "sd-json.h" #include "build.h" #include "bus-internal.h" #include "bus-util.h" #include "errno-util.h" -#include "format-table.h" #include "io-util.h" #include "log.h" #include "main-func.h" -#include "options.h" #include "parse-argument.h" #include "time-util.h" +#include "verbs.h" static const char *arg_bus_path = NULL; static BusTransport arg_transport = BUS_TRANSPORT_LOCAL; static RuntimeScope arg_runtime_scope = RUNTIME_SCOPE_SYSTEM; static bool arg_quiet = false; -static int help(void) { - _cleanup_(table_unrefp) Table *options = NULL; - int r; - - r = option_parser_get_help_table(&options); - if (r < 0) - return r; - - printf("%s [OPTIONS...]\n" - "\nForward messages between a pipe or socket and a D-Bus bus.\n\n", - program_invocation_short_name); - - r = table_print_or_warn(options); - if (r < 0) - return r; - - return 0; -} +COMMAND( + "systemd-stdio-bridge\0", + "Forward messages between a pipe or socket and a D-Bus bus.", + .man_pages = "systemd-stdio-bridge.1\0", +); static int parse_argv(int argc, char *argv[]) { assert(argc >= 0); @@ -53,7 +40,7 @@ static int parse_argv(int argc, char *argv[]) { switch (c) { OPTION_COMMON_HELP: - return help(); + return command_print_help("systemd-stdio-bridge"); OPTION_COMMON_VERSION: return version(); @@ -80,6 +67,9 @@ static int parse_argv(int argc, char *argv[]) { OPTION('q', "quiet", NULL, "Fail silently instead of logging errors"): arg_quiet = true; break; + + OPTION_COMMON_INTROSPECT_CLI: + return introspect_cli(SD_JSON_FORMAT_OFF); } if (option_parser_get_n_args(&opts) > 0) diff --git a/test/units/TEST-74-AUX-UTILS.introspect-cli.sh b/test/units/TEST-74-AUX-UTILS.introspect-cli.sh index af025e4585..cc1009b079 100755 --- a/test/units/TEST-74-AUX-UTILS.introspect-cli.sh +++ b/test/units/TEST-74-AUX-UTILS.introspect-cli.sh @@ -50,6 +50,7 @@ INTROSPECTABLE=( systemd-pty-forward systemd-socket-activate systemd-socket-proxyd + systemd-stdio-bridge systemd-tty-ask-password-agent systemd-veritysetup timedatectl From eb6297012d56773571acaeebfe453e2fed623eeb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Zbigniew=20J=C4=99drzejewski-Szmek?= Date: Sun, 16 Aug 2026 14:39:07 +0200 Subject: [PATCH 15/24] systemd-storage-block: convert to VERB_COMMAND and add --introspect-cli The custom help() is replaced by a COMMAND() description, and --introspect-cli is added. The abstract is changed from the noun phrase "Simple block device backed storage provider" to the imperative "Expose local block devices as storage volumes.", following the man page and consistent with the other commands. --- src/storage/storage-block.c | 36 ++++++------------- .../units/TEST-74-AUX-UTILS.introspect-cli.sh | 1 + 2 files changed, 12 insertions(+), 25 deletions(-) diff --git a/src/storage/storage-block.c b/src/storage/storage-block.c index b33bf9ce40..363fad66da 100644 --- a/src/storage/storage-block.c +++ b/src/storage/storage-block.c @@ -13,18 +13,22 @@ #include "device-util.h" #include "errno-util.h" #include "fd-util.h" -#include "format-table.h" #include "hashmap.h" -#include "help-util.h" #include "json-util.h" #include "log.h" #include "main-func.h" -#include "options.h" #include "path-util.h" #include "storage-util.h" #include "strv.h" #include "varlink-io.systemd.StorageProvider.h" #include "varlink-util.h" +#include "verbs.h" + +COMMAND( + "systemd-storage-block\0", + "Expose local block devices as storage volumes.", + .man_pages = "systemd-storage-block.8\0", +); static int block_device_pick_name( const BlockDevice *d, @@ -382,27 +386,6 @@ static int vl_server(void) { return 0; } -static int help(void) { - int r; - - help_cmdline("[OPTIONS...]"); - help_abstract("Simple block device backed storage provider"); - - _cleanup_(table_unrefp) Table *options = NULL; - r = option_parser_get_help_table(&options); - if (r < 0) - return r; - - help_section("Options"); - - r = table_print_or_warn(options); - if (r < 0) - return r; - - help_man_page_reference("systemd-storage-block", "8"); - return 0; -} - static int parse_argv(int argc, char *argv[]) { assert(argc >= 0); assert(argv); @@ -412,10 +395,13 @@ static int parse_argv(int argc, char *argv[]) { switch (c) { OPTION_COMMON_HELP: - return help(); + return command_print_help("systemd-storage-block"); OPTION_COMMON_VERSION: return version(); + + OPTION_COMMON_INTROSPECT_CLI: + return introspect_cli(SD_JSON_FORMAT_OFF); } if (option_parser_get_n_args(&opts) > 0) diff --git a/test/units/TEST-74-AUX-UTILS.introspect-cli.sh b/test/units/TEST-74-AUX-UTILS.introspect-cli.sh index cc1009b079..85bb2e0e62 100755 --- a/test/units/TEST-74-AUX-UTILS.introspect-cli.sh +++ b/test/units/TEST-74-AUX-UTILS.introspect-cli.sh @@ -51,6 +51,7 @@ INTROSPECTABLE=( systemd-socket-activate systemd-socket-proxyd systemd-stdio-bridge + systemd-storage-block systemd-tty-ask-password-agent systemd-veritysetup timedatectl From de4804f0da0a11b7c0841dd00a72f640b7c61bde Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Zbigniew=20J=C4=99drzejewski-Szmek?= Date: Sun, 16 Aug 2026 14:39:35 +0200 Subject: [PATCH 16/24] systemd-storage-fs: convert to VERB_COMMAND and add --introspect-cli The custom help() is replaced by a COMMAND() description, and --introspect-cli is added. The abstract is changed from the noun phrase "Simple file system backed storage provider" to the imperative "Expose regular files and directories as storage volumes.", following the man page and consistent with the other commands. --- src/storage/storage-fs.c | 36 ++++++------------- .../units/TEST-74-AUX-UTILS.introspect-cli.sh | 1 + 2 files changed, 12 insertions(+), 25 deletions(-) diff --git a/src/storage/storage-fs.c b/src/storage/storage-fs.c index 5b2bc415fb..ef0cc7b66c 100644 --- a/src/storage/storage-fs.c +++ b/src/storage/storage-fs.c @@ -18,14 +18,11 @@ #include "device-util.h" #include "errno-util.h" #include "fd-util.h" -#include "format-table.h" #include "fs-util.h" #include "hashmap.h" -#include "help-util.h" #include "log.h" #include "main-func.h" #include "mount-util.h" -#include "options.h" #include "path-lookup.h" #include "path-util.h" #include "recurse-dir.h" @@ -37,9 +34,16 @@ #include "uid-classification.h" #include "varlink-io.systemd.StorageProvider.h" #include "varlink-util.h" +#include "verbs.h" static RuntimeScope arg_runtime_scope = RUNTIME_SCOPE_SYSTEM; +COMMAND( + "systemd-storage-fs\0", + "Expose regular files and directories as storage volumes.", + .man_pages = "systemd-storage-fs.8\0", +); + /* For now we maintain a simple, compiled-in list of templates. One of those days we might want to move these * into configurable drop-in files on disk. */ typedef enum Template { @@ -742,27 +746,6 @@ static int vl_server(void) { return 0; } -static int help(void) { - int r; - - help_cmdline("[OPTIONS...]"); - help_abstract("Simple file system backed storage provider"); - - _cleanup_(table_unrefp) Table *options = NULL; - r = option_parser_get_help_table(&options); - if (r < 0) - return r; - - help_section("Options"); - - r = table_print_or_warn(options); - if (r < 0) - return r; - - help_man_page_reference("systemd-storage-fs", "8"); - return 0; -} - static int parse_argv(int argc, char *argv[]) { assert(argc >= 0); assert(argv); @@ -772,7 +755,7 @@ static int parse_argv(int argc, char *argv[]) { switch (c) { OPTION_COMMON_HELP: - return help(); + return command_print_help("systemd-storage-fs"); OPTION_COMMON_VERSION: return version(); @@ -784,6 +767,9 @@ static int parse_argv(int argc, char *argv[]) { OPTION_COMMON_USER: arg_runtime_scope = RUNTIME_SCOPE_USER; break; + + OPTION_COMMON_INTROSPECT_CLI: + return introspect_cli(SD_JSON_FORMAT_OFF); } if (option_parser_get_n_args(&opts) > 0) diff --git a/test/units/TEST-74-AUX-UTILS.introspect-cli.sh b/test/units/TEST-74-AUX-UTILS.introspect-cli.sh index 85bb2e0e62..365c93b49b 100755 --- a/test/units/TEST-74-AUX-UTILS.introspect-cli.sh +++ b/test/units/TEST-74-AUX-UTILS.introspect-cli.sh @@ -52,6 +52,7 @@ INTROSPECTABLE=( systemd-socket-proxyd systemd-stdio-bridge systemd-storage-block + systemd-storage-fs systemd-tty-ask-password-agent systemd-veritysetup timedatectl From e70f197e37924b02e353b93eb825d19f9bcd9104 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Zbigniew=20J=C4=99drzejewski-Szmek?= Date: Sun, 16 Aug 2026 14:40:03 +0200 Subject: [PATCH 17/24] storagetm: convert to VERB_COMMAND and add --introspect-cli The custom help() is replaced by a COMMAND() description, and --introspect-cli is added. --- src/storagetm/storagetm.c | 43 ++++++------------- .../units/TEST-74-AUX-UTILS.introspect-cli.sh | 1 + 2 files changed, 14 insertions(+), 30 deletions(-) diff --git a/src/storagetm/storagetm.c b/src/storagetm/storagetm.c index 2d59ab1408..3402212cb5 100644 --- a/src/storagetm/storagetm.c +++ b/src/storagetm/storagetm.c @@ -5,6 +5,7 @@ #include #include "sd-event.h" +#include "sd-json.h" #include "sd-netlink.h" #include "af-list.h" @@ -16,18 +17,16 @@ #include "device-util.h" #include "fd-util.h" #include "fileio.h" -#include "format-table.h" #include "fs-util.h" +#include "glyph-util.h" #include "hashmap.h" #include "id128-util.h" #include "local-addresses.h" #include "main-func.h" #include "mountpoint-util.h" -#include "options.h" #include "os-util.h" #include "path-util.h" #include "plymouth-util.h" -#include "pretty-print.h" #include "random-util.h" #include "recurse-dir.h" #include "siphash24.h" @@ -38,6 +37,7 @@ #include "terminal-util.h" #include "time-util.h" #include "udev-util.h" +#include "verbs.h" static char **arg_devices = NULL; static char *arg_nqn = NULL; @@ -46,32 +46,12 @@ static int arg_all = 0; STATIC_DESTRUCTOR_REGISTER(arg_devices, strv_freep); STATIC_DESTRUCTOR_REGISTER(arg_nqn, freep); -static int help(void) { - _cleanup_free_ char *link = NULL; - _cleanup_(table_unrefp) Table *options = NULL; - int r; - - r = terminal_urlify_man("systemd-storagetm", "8", &link); - if (r < 0) - return log_oom(); - - r = option_parser_get_help_table(&options); - if (r < 0) - return r; - - printf("%s [OPTIONS...] [DEVICE...]\n" - "\n%sExpose a block device or regular file as NVMe-TCP volume.%s\n\n", - program_invocation_short_name, - ansi_highlight(), - ansi_normal()); - - r = table_print_or_warn(options); - if (r < 0) - return r; - - printf("\nSee the %s for details.\n", link); - return 0; -} +COMMAND( + "systemd-storagetm\0", + "Expose a block device or regular file as NVMe-TCP volume.", + .argspec = "[DEVICE…]\0", + .man_pages = "systemd-storagetm.8\0", +); static int parse_argv(int argc, char *argv[]) { assert(argc >= 0); @@ -84,7 +64,7 @@ static int parse_argv(int argc, char *argv[]) { switch (c) { OPTION_COMMON_HELP: - return help(); + return command_print_help("systemd-storagetm"); OPTION_COMMON_VERSION: return version(); @@ -110,6 +90,9 @@ static int parse_argv(int argc, char *argv[]) { return r; return 0; + + OPTION_COMMON_INTROSPECT_CLI: + return introspect_cli(SD_JSON_FORMAT_OFF); } char **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 365c93b49b..89f0be5001 100755 --- a/test/units/TEST-74-AUX-UTILS.introspect-cli.sh +++ b/test/units/TEST-74-AUX-UTILS.introspect-cli.sh @@ -53,6 +53,7 @@ INTROSPECTABLE=( systemd-stdio-bridge systemd-storage-block systemd-storage-fs + systemd-storagetm systemd-tty-ask-password-agent systemd-veritysetup timedatectl From 2805c192f94ab8fa3718c3f6a71b3750542962e5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Zbigniew=20J=C4=99drzejewski-Szmek?= Date: Sun, 16 Aug 2026 14:40:26 +0200 Subject: [PATCH 18/24] sysinstall: convert to VERB_COMMAND 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 a COMMAND() description, and --introspect-cli is added. The abstract is changed to use the imperative form ("Install …" instead of "Installs …"), consistent with the other commands. This also fixes the doubled colon in the "Options::" heading. --- src/sysinstall/sysinstall.c | 37 +++++++------------ .../units/TEST-74-AUX-UTILS.introspect-cli.sh | 1 + 2 files changed, 14 insertions(+), 24 deletions(-) diff --git a/src/sysinstall/sysinstall.c b/src/sysinstall/sysinstall.c index b3b730b914..13269999a2 100644 --- a/src/sysinstall/sysinstall.c +++ b/src/sysinstall/sysinstall.c @@ -4,6 +4,7 @@ #include #include +#include "sd-json.h" #include "sd-varlink.h" #include "alloc-util.h" @@ -27,7 +28,6 @@ #include "fs-util.h" #include "glyph-util.h" #include "hashmap.h" -#include "help-util.h" #include "image-policy.h" #include "json-util.h" #include "locale-setup.h" @@ -36,7 +36,6 @@ #include "machine-credential.h" #include "main-func.h" #include "mount-util.h" -#include "options.h" #include "os-util.h" #include "parse-argument.h" #include "parse-util.h" @@ -47,6 +46,7 @@ #include "terminal-util.h" #include "varlink-io.systemd.SysInstall.h" #include "varlink-util.h" +#include "verbs.h" static char *arg_node = NULL; static bool arg_welcome = true; @@ -70,6 +70,13 @@ STATIC_DESTRUCTOR_REGISTER(arg_definitions, strv_freep); STATIC_DESTRUCTOR_REGISTER(arg_kernel_image, freep); STATIC_DESTRUCTOR_REGISTER(arg_credentials, machine_credential_context_done); +COMMAND( + "systemd-sysinstall\0", + "Install the OS to another block device.", + .argspec = "[DEVICE]\0", + .man_pages = "systemd-sysinstall.8\0", +); + typedef enum ProgressPhase { PROGRESS_ENCRYPT_CREDENTIALS, PROGRESS_INSTALL_PARTITIONS, @@ -155,27 +162,6 @@ static void sysinstall_context_done(SysInstallContext *c) { sd_varlink_unref(c->link); } -static int help(void) { - int r; - - _cleanup_(table_unrefp) Table *options = NULL; - r = option_parser_get_help_table(&options); - if (r < 0) - return r; - - help_cmdline("[OPTIONS...] [DEVICE]"); - help_abstract("Installs the OS to another block device."); - help_section("Options:"); - - r = table_print_or_warn(options); - if (r < 0) - return r; - - help_man_page_reference("systemd-sysinstall", "8"); - - return 0; -} - static int parse_argv(int argc, char *argv[]) { int r; @@ -188,7 +174,7 @@ static int parse_argv(int argc, char *argv[]) { switch (c) { OPTION_COMMON_HELP: - return help(); + return command_print_help("systemd-sysinstall"); OPTION_COMMON_VERSION: return version(); @@ -289,6 +275,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); } char **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 89f0be5001..5e4a73be46 100755 --- a/test/units/TEST-74-AUX-UTILS.introspect-cli.sh +++ b/test/units/TEST-74-AUX-UTILS.introspect-cli.sh @@ -54,6 +54,7 @@ INTROSPECTABLE=( systemd-storage-block systemd-storage-fs systemd-storagetm + systemd-sysinstall systemd-tty-ask-password-agent systemd-veritysetup timedatectl From 9f402dcce52096e84ff0e8c5a0c33e7906e3e67f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Zbigniew=20J=C4=99drzejewski-Szmek?= Date: Sun, 16 Aug 2026 14:40:46 +0200 Subject: [PATCH 19/24] tpm2-clear: convert to VERB_COMMAND and add --introspect-cli The custom help() is replaced by a COMMAND() description, and --introspect-cli is added. The man page reference is fixed to systemd-tpm2-clear.service(8), the only name that page is known by. --- src/tpm2-setup/tpm2-clear.c | 45 +++++-------------- .../units/TEST-74-AUX-UTILS.introspect-cli.sh | 1 + 2 files changed, 12 insertions(+), 34 deletions(-) diff --git a/src/tpm2-setup/tpm2-clear.c b/src/tpm2-setup/tpm2-clear.c index fcef2b76bb..de0959572b 100644 --- a/src/tpm2-setup/tpm2-clear.c +++ b/src/tpm2-setup/tpm2-clear.c @@ -1,51 +1,25 @@ /* SPDX-License-Identifier: LGPL-2.1-or-later */ +#include "sd-json.h" #include "sd-messages.h" -#include "alloc-util.h" #include "build.h" #include "dlopen-note.h" #include "env-util.h" #include "fileio.h" -#include "format-table.h" #include "log.h" #include "main-func.h" -#include "options.h" -#include "pretty-print.h" #include "proc-cmdline.h" #include "tpm2-util.h" +#include "verbs.h" static bool arg_graceful = false; -static int help(void) { - _cleanup_free_ char *link = NULL; - _cleanup_(table_unrefp) Table *options = NULL; - int r; - - r = terminal_urlify_man("systemd-tpm2-clear", "8", &link); - if (r < 0) - return log_oom(); - - r = option_parser_get_help_table(&options); - if (r < 0) - return r; - - printf("%s [OPTIONS...]\n" - "\n%sRequest clearing of the TPM2 from PC firmware.%s\n" - "\n%sOptions:%s\n", - program_invocation_short_name, - ansi_highlight(), - ansi_normal(), - ansi_underline(), - ansi_normal()); - - r = table_print_or_warn(options); - if (r < 0) - return r; - - printf("\nSee the %s for details.\n", link); - return 0; -} +COMMAND( + "systemd-tpm2-clear\0", + "Request clearing of the TPM2 from PC firmware.", + .man_pages = "systemd-tpm2-clear.service.8\0", +); static int parse_argv(int argc, char *argv[]) { assert(argc >= 0); @@ -57,7 +31,7 @@ static int parse_argv(int argc, char *argv[]) { switch (c) { OPTION_COMMON_HELP: - return help(); + return command_print_help("systemd-tpm2-clear"); OPTION_COMMON_VERSION: return version(); @@ -65,6 +39,9 @@ static int parse_argv(int argc, char *argv[]) { OPTION_LONG("graceful", NULL, "Exit gracefully if no TPM2 device is found"): arg_graceful = true; break; + + OPTION_COMMON_INTROSPECT_CLI: + return introspect_cli(SD_JSON_FORMAT_OFF); } if (option_parser_get_n_args(&opts) != 0) diff --git a/test/units/TEST-74-AUX-UTILS.introspect-cli.sh b/test/units/TEST-74-AUX-UTILS.introspect-cli.sh index 5e4a73be46..1d13287637 100755 --- a/test/units/TEST-74-AUX-UTILS.introspect-cli.sh +++ b/test/units/TEST-74-AUX-UTILS.introspect-cli.sh @@ -55,6 +55,7 @@ INTROSPECTABLE=( systemd-storage-fs systemd-storagetm systemd-sysinstall + systemd-tpm2-clear systemd-tty-ask-password-agent systemd-veritysetup timedatectl From 3e7b76f67254d9330c377cef7d0d03b3cb372db1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Zbigniew=20J=C4=99drzejewski-Szmek?= Date: Sun, 16 Aug 2026 14:41:04 +0200 Subject: [PATCH 20/24] tpm2-setup: convert to VERB_COMMAND and add --introspect-cli The custom help() is replaced by a COMMAND() description, and --introspect-cli is added. --- src/tpm2-setup/tpm2-setup.c | 48 +++++-------------- .../units/TEST-74-AUX-UTILS.introspect-cli.sh | 1 + 2 files changed, 14 insertions(+), 35 deletions(-) diff --git a/src/tpm2-setup/tpm2-setup.c b/src/tpm2-setup/tpm2-setup.c index 2b247efd23..9435147778 100644 --- a/src/tpm2-setup/tpm2-setup.c +++ b/src/tpm2-setup/tpm2-setup.c @@ -4,6 +4,8 @@ #include #include +#include "sd-id128.h" +#include "sd-json.h" #include "sd-messages.h" #include "alloc-util.h" @@ -17,23 +19,20 @@ #include "errno-util.h" #include "fd-util.h" #include "fileio.h" -#include "format-table.h" #include "fs-util.h" #include "hexdecoct.h" #include "log.h" #include "main-func.h" #include "mkdir.h" -#include "options.h" #include "parse-util.h" #include "path-util.h" -#include "pretty-print.h" -#include "sd-id128.h" #include "set.h" #include "sort-util.h" #include "string-util.h" #include "strv.h" #include "tmpfile-util.h" #include "tpm2-util.h" +#include "verbs.h" static char *arg_tpm2_device = NULL; static bool arg_early = false; @@ -41,42 +40,18 @@ static bool arg_graceful = false; STATIC_DESTRUCTOR_REGISTER(arg_tpm2_device, freep); +COMMAND( + "systemd-tpm2-setup\0", + "Set up the TPM2 Storage Root Key (SRK) and Endorsement Key (EK), and initialize NvPCRs.", + .man_pages = "systemd-tpm2-setup.8\0", +); + #define TPM2_SRK_PEM_PERSISTENT_PATH "/var/lib/systemd/tpm2-srk-public-key.pem" #define TPM2_SRK_PEM_RUNTIME_PATH "/run/systemd/tpm2-srk-public-key.pem" #define TPM2_SRK_TPM2B_PUBLIC_PERSISTENT_PATH "/var/lib/systemd/tpm2-srk-public-key.tpm2b_public" #define TPM2_SRK_TPM2B_PUBLIC_RUNTIME_PATH "/run/systemd/tpm2-srk-public-key.tpm2b_public" -static int help(void) { - _cleanup_free_ char *link = NULL; - _cleanup_(table_unrefp) Table *options = NULL; - int r; - - r = terminal_urlify_man("systemd-tpm2-setup", "8", &link); - if (r < 0) - return log_oom(); - - r = option_parser_get_help_table(&options); - if (r < 0) - return r; - - printf("%s [OPTIONS...]\n" - "\n%sSet up the TPM2 Storage Root Key (SRK) and Endorsement Key (EK), and initialize NvPCRs.%s\n" - "\n%sOptions:%s\n", - program_invocation_short_name, - ansi_highlight(), - ansi_normal(), - ansi_underline(), - ansi_normal()); - - r = table_print_or_warn(options); - if (r < 0) - return r; - - printf("\nSee the %s for details.\n", link); - return 0; -} - static int parse_argv(int argc, char *argv[]) { int r; @@ -89,7 +64,7 @@ static int parse_argv(int argc, char *argv[]) { switch (c) { OPTION_COMMON_HELP: - return help(); + return command_print_help("systemd-tpm2-setup"); OPTION_COMMON_VERSION: return version(); @@ -113,6 +88,9 @@ static int parse_argv(int argc, char *argv[]) { OPTION_LONG("graceful", NULL, "Exit gracefully if no TPM2 device is found"): arg_graceful = true; break; + + OPTION_COMMON_INTROSPECT_CLI: + return introspect_cli(SD_JSON_FORMAT_OFF); } if (option_parser_get_n_args(&opts) != 0) diff --git a/test/units/TEST-74-AUX-UTILS.introspect-cli.sh b/test/units/TEST-74-AUX-UTILS.introspect-cli.sh index 1d13287637..769506277b 100755 --- a/test/units/TEST-74-AUX-UTILS.introspect-cli.sh +++ b/test/units/TEST-74-AUX-UTILS.introspect-cli.sh @@ -56,6 +56,7 @@ INTROSPECTABLE=( systemd-storagetm systemd-sysinstall systemd-tpm2-clear + systemd-tpm2-setup systemd-tty-ask-password-agent systemd-veritysetup timedatectl From 760bd23cece4bcaf49f0d2df5a225b43158c1e67 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Zbigniew=20J=C4=99drzejewski-Szmek?= Date: Sun, 16 Aug 2026 14:41:22 +0200 Subject: [PATCH 21/24] update-done: convert to VERB_COMMAND and add --introspect-cli The custom help() is replaced by a COMMAND() description, and --introspect-cli is added. --- src/update-done/update-done.c | 46 ++++++------------- .../units/TEST-74-AUX-UTILS.introspect-cli.sh | 1 + 2 files changed, 14 insertions(+), 33 deletions(-) diff --git a/src/update-done/update-done.c b/src/update-done/update-done.c index ceb8337495..c3545e1b57 100644 --- a/src/update-done/update-done.c +++ b/src/update-done/update-done.c @@ -2,6 +2,8 @@ #include +#include "sd-json.h" + #include "alloc-util.h" #include "build.h" #include "chase.h" @@ -9,20 +11,24 @@ #include "errno-util.h" #include "fd-util.h" #include "fileio.h" -#include "format-table.h" #include "label-util.h" #include "log.h" #include "main-func.h" -#include "options.h" #include "parse-argument.h" -#include "pretty-print.h" #include "string-util.h" #include "time-util.h" +#include "verbs.h" static char *arg_root = NULL; STATIC_DESTRUCTOR_REGISTER(arg_root, freep); +COMMAND( + "systemd-update-done\0", + "Mark /etc/ and /var/ as fully updated.", + .man_pages = "systemd-update-done.8\0", +); + static int save_timestamp(const char *dir, struct timespec *ts) { _cleanup_free_ char *message = NULL, *dirpath = NULL; _cleanup_close_ int fd = -EBADF; @@ -62,35 +68,6 @@ static int save_timestamp(const char *dir, struct timespec *ts) { return 0; } -static int help(void) { - _cleanup_free_ char *link = NULL; - _cleanup_(table_unrefp) Table *options = NULL; - int r; - - r = terminal_urlify_man("systemd-update-done", "8", &link); - if (r < 0) - return log_oom(); - - r = option_parser_get_help_table(&options); - if (r < 0) - return r; - - printf("%s [OPTIONS...]\n" - "\n%sMark /etc/ and /var/ as fully updated.%s\n" - "\n%sOptions:%s\n", - program_invocation_short_name, - ansi_highlight(), - ansi_normal(), - ansi_underline(), - ansi_normal()); - r = table_print_or_warn(options); - if (r < 0) - return r; - - printf("\nSee the %s for details.\n", link); - return 0; -} - static int parse_argv(int argc, char *argv[]) { int r; @@ -102,7 +79,7 @@ static int parse_argv(int argc, char *argv[]) { FOREACH_OPTION_OR_RETURN(c, &opts) switch (c) { OPTION_COMMON_HELP: - return help(); + return command_print_help("systemd-update-done"); OPTION_COMMON_VERSION: return version(); @@ -112,6 +89,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) > 0) diff --git a/test/units/TEST-74-AUX-UTILS.introspect-cli.sh b/test/units/TEST-74-AUX-UTILS.introspect-cli.sh index 769506277b..82eac04564 100755 --- a/test/units/TEST-74-AUX-UTILS.introspect-cli.sh +++ b/test/units/TEST-74-AUX-UTILS.introspect-cli.sh @@ -58,6 +58,7 @@ INTROSPECTABLE=( systemd-tpm2-clear systemd-tpm2-setup systemd-tty-ask-password-agent + systemd-update-done systemd-veritysetup timedatectl varlinkctl From 9cf35bfa4da95167007c4f935c70904bb31e6213 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Zbigniew=20J=C4=99drzejewski-Szmek?= Date: Sun, 16 Aug 2026 14:41:41 +0200 Subject: [PATCH 22/24] validatefs: convert to VERB_COMMAND and add --introspect-cli The custom help() is replaced by a COMMAND() description, and --introspect-cli is added. The positional argument is spelled MOUNTPOINT in the synopsis, like the other metavars, instead of /path/to/mountpoint, and the man page synopsis, which said "[DEVICE]", is fixed to match: the argument is required and it is a mount point. --- man/systemd-validatefs@.service.xml | 2 +- src/validatefs/validatefs.c | 42 ++++++------------- .../units/TEST-74-AUX-UTILS.introspect-cli.sh | 1 + 3 files changed, 14 insertions(+), 31 deletions(-) diff --git a/man/systemd-validatefs@.service.xml b/man/systemd-validatefs@.service.xml index 85554a30de..030078e1e2 100644 --- a/man/systemd-validatefs@.service.xml +++ b/man/systemd-validatefs@.service.xml @@ -23,7 +23,7 @@ systemd-validatefs@.service - /usr/lib/systemd/systemd-validatefs DEVICE + /usr/lib/systemd/systemd-validatefs MOUNTPOINT diff --git a/src/validatefs/validatefs.c b/src/validatefs/validatefs.c index 1ac025b74d..179274b43d 100644 --- a/src/validatefs/validatefs.c +++ b/src/validatefs/validatefs.c @@ -1,6 +1,7 @@ /* SPDX-License-Identifier: LGPL-2.1-or-later */ #include "sd-device.h" +#include "sd-json.h" #include "alloc-util.h" #include "blkid-util.h" @@ -11,19 +12,17 @@ #include "dlopen-note.h" #include "errno-util.h" #include "fd-util.h" -#include "format-table.h" #include "gpt.h" #include "initrd-util.h" #include "log.h" #include "main-func.h" #include "mountpoint-util.h" -#include "options.h" #include "parse-argument.h" #include "path-util.h" -#include "pretty-print.h" #include "string-util.h" #include "strv.h" #include "utf8.h" +#include "verbs.h" #include "xattr-util.h" static char *arg_target = NULL; @@ -32,32 +31,12 @@ static char *arg_root = NULL; STATIC_DESTRUCTOR_REGISTER(arg_target, freep); STATIC_DESTRUCTOR_REGISTER(arg_root, freep); -static int help(void) { - _cleanup_free_ char *link = NULL; - _cleanup_(table_unrefp) Table *options = NULL; - int r; - - r = terminal_urlify_man("systemd-validatefs@.service", "8", &link); - if (r < 0) - return log_oom(); - - r = option_parser_get_help_table(&options); - if (r < 0) - return r; - - printf("%s [OPTIONS...] /path/to/mountpoint\n" - "\n%sCheck file system validation constraints.%s\n" - "\nOptions:\n", - program_invocation_short_name, - ansi_highlight(), - ansi_normal()); - r = table_print_or_warn(options); - if (r < 0) - return r; - - printf("\nSee the %s for details.\n", link); - return 0; -} +COMMAND( + "systemd-validatefs\0", + "Check file system validation constraints.", + .argspec = "MOUNTPOINT\0", + .man_pages = "systemd-validatefs@.service.8\0", +); static int parse_argv(int argc, char *argv[]) { int r; @@ -70,7 +49,7 @@ static int parse_argv(int argc, char *argv[]) { FOREACH_OPTION_OR_RETURN(c, &opts) switch (c) { OPTION_COMMON_HELP: - return help(); + return command_print_help("systemd-validatefs"); OPTION_COMMON_VERSION: return version(); @@ -88,6 +67,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); } char **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 82eac04564..18cbe2c806 100755 --- a/test/units/TEST-74-AUX-UTILS.introspect-cli.sh +++ b/test/units/TEST-74-AUX-UTILS.introspect-cli.sh @@ -59,6 +59,7 @@ INTROSPECTABLE=( systemd-tpm2-setup systemd-tty-ask-password-agent systemd-update-done + systemd-validatefs systemd-veritysetup timedatectl varlinkctl From 5eb3bf51825f4606c4ff4e8efa8f11dbcb5253a7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Zbigniew=20J=C4=99drzejewski-Szmek?= Date: Sun, 16 Aug 2026 15:29:39 +0200 Subject: [PATCH 23/24] storagetm: make the synopsis more accurate MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit parse_argv() requires at least one DEVICE unless --all is given, and refuses DEVICE arguments when --all is given, so "[DEVICE…]" described neither mode correctly. Spell out the three modes as separate synopsis lines, like pcrextend and inhibit do, and update the man page to match. --- man/systemd-storagetm.service.xml | 16 +++++++++++++--- src/storagetm/storagetm.c | 5 ++++- 2 files changed, 17 insertions(+), 4 deletions(-) diff --git a/man/systemd-storagetm.service.xml b/man/systemd-storagetm.service.xml index 6172190157..b49a27feb0 100644 --- a/man/systemd-storagetm.service.xml +++ b/man/systemd-storagetm.service.xml @@ -28,7 +28,17 @@ /usr/lib/systemd/systemd-storagetm OPTIONS - DEVICE + DEVICE + + + /usr/lib/systemd/systemd-storagetm + OPTIONS + --all + + + /usr/lib/systemd/systemd-storagetm + OPTIONS + --list-devices @@ -53,8 +63,8 @@ ip=dhcp to assign routable addresses too. For further details see systemd-network-generator.service8. - Unless the switch is used expects one or more block devices or regular files to expose - via NVMe-TCP as argument. + Unless the or switches are used, one or more + block devices or regular files to expose via NVMe-TCP must be specified as arguments. diff --git a/src/storagetm/storagetm.c b/src/storagetm/storagetm.c index 3402212cb5..91a4763307 100644 --- a/src/storagetm/storagetm.c +++ b/src/storagetm/storagetm.c @@ -49,7 +49,10 @@ STATIC_DESTRUCTOR_REGISTER(arg_nqn, freep); COMMAND( "systemd-storagetm\0", "Expose a block device or regular file as NVMe-TCP volume.", - .argspec = "[DEVICE…]\0", + .argspec = + "DEVICE…\0" + "--all\0" + "--list-devices\0", .man_pages = "systemd-storagetm.8\0", ); From fb4a092b9afa89e27057797a5fa876fc490ad2e9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Zbigniew=20J=C4=99drzejewski-Szmek?= Date: Sun, 16 Aug 2026 15:33:40 +0200 Subject: [PATCH 24/24] test: check that systemd-clonesetup's verbs are described by --introspect-cli Same as for systemd-hwdb and systemd-id128. --- test/units/TEST-74-AUX-UTILS.introspect-cli.sh | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/test/units/TEST-74-AUX-UTILS.introspect-cli.sh b/test/units/TEST-74-AUX-UTILS.introspect-cli.sh index 18cbe2c806..a2b8983efa 100755 --- a/test/units/TEST-74-AUX-UTILS.introspect-cli.sh +++ b/test/units/TEST-74-AUX-UTILS.introspect-cli.sh @@ -89,6 +89,10 @@ fi systemd-id128 --introspect-cli | jq -e \ '.commands[0].verbs | map(.names[0]) | contains(["new", "machine-id", "show", "help"])' +# systemd-clonesetup defines verbs, check that they are described +systemd-clonesetup --introspect-cli | jq -e \ + '.commands[0].verbs | map(.names[0]) | sort == ["add", "remove"]' + # resolvectl is a multicall binary, check that both commands are described resolvectl --introspect-cli | jq -e \ '[.commands[].names[0]] | sort == ["resolvconf", "resolvectl", "systemd-resolve"]'