core: convert to VERB_COMMAND and add --introspect-cli

--introspect-cli follows the same deferred pattern as --help: parse_argv()
only records the action, and it is executed in the same place where
ACTION_HELP and ACTION_VERSION are handled.

The help is looked up by explicit name: in the multicall configuration
the systemd and systemd-executor commands share one binary, so the
"first COMMAND" default would depend on link order.

The "This program takes no positional arguments." line is dropped from
the help output — the default "[OPTION…]" synopsis already says so.
(And also, this is not quite true. It does take positional args when
called as init.)
This commit is contained in:
Zbigniew Jędrzejewski-Szmek
2026-08-26 12:22:15 +02:00
parent 044ebd7e54
commit 4f2b0e977c
4 changed files with 32 additions and 27 deletions
+1
View File
@@ -37,6 +37,7 @@ int proc_cmdline_filter_pid1_args(char **argv, char ***ret) {
{ "no-pager", no_argument, },
{ "help", no_argument, },
{ "version", no_argument, },
{ "introspect-cli", no_argument, },
{ "dump-configuration-items", no_argument, },
{ "dump-bus-properties", no_argument, },
{ "bus-introspect", required_argument },
+22 -26
View File
@@ -51,13 +51,11 @@
#include "fd-util.h"
#include "fdset.h"
#include "fileio.h"
#include "format-table.h"
#include "format-util.h"
#include "fs-util.h"
#include "glyph-util.h"
#include "hash-funcs.h"
#include "hashmap.h"
#include "help-util.h"
#include "hexdecoct.h"
#include "hostname-setup.h"
#include "id128-util.h"
@@ -119,6 +117,7 @@
#include "unit-name.h"
#include "user-util.h"
#include "utf8.h"
#include "verbs.h"
#include "version.h"
#include "virt.h"
#include "watchdog.h"
@@ -131,6 +130,7 @@ static enum {
ACTION_RUN,
ACTION_HELP,
ACTION_VERSION,
ACTION_INTROSPECT_CLI,
ACTION_TEST,
ACTION_DUMP_CONFIGURATION_ITEMS,
ACTION_DUMP_BUS_PROPERTIES,
@@ -1039,6 +1039,14 @@ static int redirect_telinit(char *argv[], char **args) {
strnull(a), strnull(b), strnull(c));
}
COMMAND(
"systemd\0",
"Start and monitor system and user services.",
.man_pages = "systemd.1\0",
.option_namespace = "systemd",
.pager_flags = &arg_pager_flags,
);
static int parse_argv(int argc, char *argv[]) {
bool user_arg_seen = false;
int r;
@@ -1068,6 +1076,10 @@ static int parse_argv(int argc, char *argv[]) {
arg_action = ACTION_VERSION;
break;
OPTION_COMMON_INTROSPECT_CLI:
arg_action = ACTION_INTROSPECT_CLI;
break;
OPTION_LONG("test", NULL, "Determine initial transaction, dump it and exit"):
arg_action = ACTION_TEST;
break;
@@ -1268,6 +1280,7 @@ static int parse_argv(int argc, char *argv[]) {
"Switch -%c is only supported when running as PID 1.", opts.opt->short_code);
/* In PID1, do nothing and continue with option parsing. */
break;
OPTION_ERROR:
if (getpid_cached() != 1)
return -EINVAL;
@@ -1290,28 +1303,6 @@ static int parse_argv(int argc, char *argv[]) {
return 0;
}
static int help(void) {
_cleanup_(table_unrefp) Table *options = NULL;
int r;
r = option_parser_get_help_table_ns("systemd", &options);
if (r < 0)
return r;
help_cmdline("[OPTIONS...]");
help_abstract("Starts and monitors system and user services.");
printf("\nThis program takes no positional arguments.\n");
help_section("Options");
r = table_print_or_warn(options);
if (r < 0)
return r;
help_man_page_reference("systemd", "1");
return 0;
}
static int prepare_reexecute(
Manager *m,
FILE **ret_f,
@@ -3745,18 +3736,23 @@ static int run_systemd(int argc, char *argv[]) {
if (r < 0)
goto finish;
if (IN_SET(arg_action, ACTION_TEST, ACTION_HELP, ACTION_DUMP_CONFIGURATION_ITEMS, ACTION_DUMP_BUS_PROPERTIES, ACTION_BUS_INTROSPECT))
if (IN_SET(arg_action, ACTION_TEST, ACTION_DUMP_CONFIGURATION_ITEMS, ACTION_DUMP_BUS_PROPERTIES, ACTION_BUS_INTROSPECT))
pager_open(arg_pager_flags);
if (arg_action != ACTION_RUN)
skip_setup = true;
if (arg_action == ACTION_HELP) {
retval = help() < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
r = command_print_help_name("systemd");
retval = r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
goto finish;
} else if (arg_action == ACTION_VERSION) {
retval = version();
goto finish;
} else if (arg_action == ACTION_INTROSPECT_CLI) {
r = introspect_cli(SD_JSON_FORMAT_OFF);
retval = r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
goto finish;
} else if (arg_action == ACTION_DUMP_CONFIGURATION_ITEMS) {
unit_dump_config_items(stdout);
retval = EXIT_SUCCESS;
+2 -1
View File
@@ -271,9 +271,10 @@ TEST(proc_cmdline_filter_pid1_args) {
"--log-color\0--log-level\0drop3\0" /* optional argument ("--log-level" is handled as another option) */
"--log-color\0accept5\0" /* optional argument (separated with space) */
"--log-color=drop4\0accept6\0" /* optional argument (concatenated with '=') */
"--introspect-cli\0accept7\0" /* no argument */
"--log-color\0--\0" /* "--" is _not_ handled as argument, and remaining strings are accepted */
"remaining\0-x\0--foo\0",
STRV_MAKE("accept1", "accept2", "accept3", "accept4", "accept5", "accept6", "remaining", "-x", "--foo"));
STRV_MAKE("accept1", "accept2", "accept3", "accept4", "accept5", "accept6", "accept7", "remaining", "-x", "--foo"));
/* test for "--" */
test_proc_cmdline_filter_pid1_args_one("systemd\0"
@@ -40,6 +40,7 @@ INTROSPECTABLE=(
shutdown
storagectl
systemctl
systemd
systemd-ac-power
systemd-analyze
systemd-ask-password
@@ -205,6 +206,12 @@ fi
systemd-id128 --introspect-cli | jq -e \
'.commands[0].verbs | map(.names[0]) | contains(["new", "machine-id", "show", "help"])'
# 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 \
'[.commands[].names[0]] | sort == ["systemd", "systemd-executor"]'
fi
if command -v systemd-mount >/dev/null; then
systemd-mount --introspect-cli | jq -e \
'[.commands[].names[0]] | sort == ["systemd-mount", "systemd-umount"]'