Add a "declarative program header" and introspection of options and verbs (#43152)
This commit is contained in:
+12
-23
@@ -1,13 +1,13 @@
|
||||
/* SPDX-License-Identifier: LGPL-2.1-or-later */
|
||||
|
||||
#include "sd-json.h"
|
||||
|
||||
#include "battery-util.h"
|
||||
#include "build.h"
|
||||
#include "format-table.h"
|
||||
#include "help-util.h"
|
||||
#include "log.h"
|
||||
#include "main-func.h"
|
||||
#include "options.h"
|
||||
#include "string-util.h"
|
||||
#include "verbs.h"
|
||||
|
||||
static bool arg_verbose = false;
|
||||
|
||||
@@ -16,25 +16,11 @@ static enum {
|
||||
ACTION_LOW,
|
||||
} arg_action = ACTION_AC_POWER;
|
||||
|
||||
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...]");
|
||||
help_abstract("Report whether we are connected to an external power source.");
|
||||
|
||||
help_section("Options");
|
||||
r = table_print_or_warn(options);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
help_man_page_reference("systemd-ac-power", "1");
|
||||
return 0;
|
||||
}
|
||||
COMMAND(
|
||||
"systemd-ac-power\0",
|
||||
"Report whether we are connected to an external power source.",
|
||||
.man_pages = "systemd-ac-power.1\0",
|
||||
);
|
||||
|
||||
static int parse_argv(int argc, char *argv[]) {
|
||||
|
||||
@@ -46,7 +32,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-ac-power");
|
||||
|
||||
OPTION_COMMON_VERSION:
|
||||
return version();
|
||||
@@ -58,6 +44,9 @@ static int parse_argv(int argc, char *argv[]) {
|
||||
OPTION_LONG("low", NULL, "Check if battery is discharging and low"):
|
||||
arg_action = ACTION_LOW;
|
||||
break;
|
||||
|
||||
OPTION_COMMON_INTROSPECT_CLI:
|
||||
return introspect_cli(SD_JSON_FORMAT_OFF);
|
||||
}
|
||||
|
||||
if (option_parser_get_n_args(&opts) > 0)
|
||||
|
||||
+11
-58
@@ -57,8 +57,6 @@
|
||||
#include "dissect-image.h"
|
||||
#include "dlopen-note.h"
|
||||
#include "extract-word.h"
|
||||
#include "format-table.h"
|
||||
#include "help-util.h"
|
||||
#include "image-policy.h"
|
||||
#include "log.h"
|
||||
#include "loop-util.h"
|
||||
@@ -198,62 +196,14 @@ static int verb_transient_settings(int argc, char *argv[], uintptr_t _data, void
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int help(void) {
|
||||
static const char *const vgroups[] = {
|
||||
"Boot Analysis",
|
||||
"Dependency Analysis",
|
||||
"Configuration Files and Search Paths",
|
||||
"Enumerate OS Concepts",
|
||||
"Expression Evaluation",
|
||||
"Clock & Time",
|
||||
"Unit & Service Analysis",
|
||||
"Executable Analysis",
|
||||
"TPM Operations",
|
||||
};
|
||||
COMMAND(
|
||||
"systemd-analyze\0",
|
||||
.abstract = "Profile systemd, show unit dependencies, check unit files.",
|
||||
.man_pages = "systemd-analyze.1\0",
|
||||
.pager_flags = &arg_pager_flags,
|
||||
);
|
||||
|
||||
Table *vtables[ELEMENTSOF(vgroups)] = {};
|
||||
CLEANUP_ELEMENTS(vtables, table_unref_array_clear);
|
||||
_cleanup_(table_unrefp) Table *options = NULL;
|
||||
int r;
|
||||
|
||||
pager_open(arg_pager_flags);
|
||||
|
||||
for (size_t i = 0; i < ELEMENTSOF(vgroups); i++) {
|
||||
r = verbs_get_help_table_group(vgroups[i], &vtables[i]);
|
||||
if (r < 0)
|
||||
return r;
|
||||
}
|
||||
|
||||
r = option_parser_get_help_table(&options);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
assert_cc(ELEMENTSOF(vtables) == 9);
|
||||
(void) table_sync_column_widths(0, options, vtables[0], vtables[1], vtables[2],
|
||||
vtables[3], vtables[4], vtables[5], vtables[6],
|
||||
vtables[7], vtables[8]);
|
||||
|
||||
help_cmdline("[OPTIONS...] COMMAND ...");
|
||||
help_abstract("Profile systemd, show unit dependencies, check unit files.");
|
||||
|
||||
for (size_t i = 0; i < ELEMENTSOF(vgroups); i++) {
|
||||
help_section(vgroups[i]);
|
||||
r = table_print_or_warn(vtables[i]);
|
||||
if (r < 0)
|
||||
return r;
|
||||
}
|
||||
|
||||
help_section("Options");
|
||||
r = table_print_or_warn(options);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
help_man_page_reference("systemd-analyze", "1");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
VERB_COMMON_HELP_HIDDEN(help);
|
||||
VERB_COMMON_HELP_AUTO_HIDDEN("systemd-analyze");
|
||||
|
||||
/* When updating this list, including descriptions, apply changes to
|
||||
* shell-completion/bash/systemd-analyze and shell-completion/zsh/_systemd-analyze too. */
|
||||
@@ -386,7 +336,7 @@ static int parse_argv(int argc, char *argv[], char ***ret_args) {
|
||||
break;
|
||||
|
||||
OPTION_COMMON_HELP:
|
||||
return help();
|
||||
return command_print_help("systemd-analyze");
|
||||
|
||||
OPTION_COMMON_VERSION:
|
||||
return version();
|
||||
@@ -610,6 +560,9 @@ static int parse_argv(int argc, char *argv[], char ***ret_args) {
|
||||
strv_free_and_replace(arg_debugger_args, l);
|
||||
break;
|
||||
}
|
||||
|
||||
OPTION_COMMON_INTROSPECT_CLI:
|
||||
return introspect_cli(arg_json_format_flags);
|
||||
}
|
||||
|
||||
_cleanup_strv_free_ char **args = strv_copy(option_parser_get_args(&opts)); /* args is [arg1, arg2, …] */
|
||||
|
||||
@@ -9,20 +9,18 @@
|
||||
#include "build.h"
|
||||
#include "bus-polkit.h"
|
||||
#include "constants.h"
|
||||
#include "format-table.h"
|
||||
#include "hashmap.h"
|
||||
#include "json-util.h"
|
||||
#include "log.h"
|
||||
#include "main-func.h"
|
||||
#include "options.h"
|
||||
#include "parse-argument.h"
|
||||
#include "pretty-print.h"
|
||||
#include "string-table.h"
|
||||
#include "string-util.h"
|
||||
#include "strv.h"
|
||||
#include "time-util.h"
|
||||
#include "varlink-io.systemd.AskPassword.h"
|
||||
#include "varlink-util.h"
|
||||
#include "verbs.h"
|
||||
|
||||
static const char *arg_icon = NULL;
|
||||
static const char *arg_id = NULL; /* identifier for 'ask-password' protocol */
|
||||
@@ -38,35 +36,14 @@ static bool arg_varlink = false;
|
||||
|
||||
STATIC_DESTRUCTOR_REGISTER(arg_message, freep);
|
||||
|
||||
static int help(void) {
|
||||
_cleanup_free_ char *link = NULL;
|
||||
_cleanup_(table_unrefp) Table *options = NULL;
|
||||
int r;
|
||||
|
||||
r = terminal_urlify_man("systemd-ask-password", "1", &link);
|
||||
if (r < 0)
|
||||
return log_oom();
|
||||
|
||||
r = option_parser_get_help_table(&options);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
printf("%s [OPTIONS...] MESSAGE\n"
|
||||
"\n%sQuery the user for a passphrase, via the TTY or a UI agent.%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-ask-password\0",
|
||||
"Query the user for a passphrase, via the TTY or a UI agent.",
|
||||
.argspec = "MESSAGE\0",
|
||||
.man_pages = "systemd-ask-password.1\0",
|
||||
);
|
||||
|
||||
static int parse_argv(int argc, char *argv[]) {
|
||||
|
||||
const char *emoji = NULL;
|
||||
int r;
|
||||
|
||||
@@ -78,7 +55,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-ask-password");
|
||||
|
||||
OPTION_COMMON_VERSION:
|
||||
return version();
|
||||
@@ -159,6 +136,9 @@ static int parse_argv(int argc, char *argv[]) {
|
||||
OPTION_SHORT('n', NULL, "Do not suffix password written to standard output with newline"):
|
||||
arg_newline = false;
|
||||
break;
|
||||
|
||||
OPTION_COMMON_INTROSPECT_CLI:
|
||||
return introspect_cli(SD_JSON_FORMAT_OFF);
|
||||
}
|
||||
|
||||
if (isempty(emoji) || streq(emoji, "auto"))
|
||||
|
||||
+43
-27
@@ -4,15 +4,14 @@
|
||||
#include <unistd.h>
|
||||
|
||||
#include "sd-device.h"
|
||||
#include "sd-json.h"
|
||||
|
||||
#include "alloc-util.h"
|
||||
#include "argv-util.h"
|
||||
#include "build.h"
|
||||
#include "device-private.h"
|
||||
#include "device-util.h"
|
||||
#include "escape.h"
|
||||
#include "fileio.h"
|
||||
#include "format-table.h"
|
||||
#include "help-util.h"
|
||||
#include "main-func.h"
|
||||
#include "parse-util.h"
|
||||
#include "percent-util.h"
|
||||
@@ -23,25 +22,12 @@
|
||||
|
||||
#define PCI_CLASS_GRAPHICS_CARD 0x30000
|
||||
|
||||
static int help(void) {
|
||||
_cleanup_(table_unrefp) Table *verbs = NULL;
|
||||
int r;
|
||||
|
||||
r = verbs_get_help_table(&verbs);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
help_cmdline("COMMAND [backlight|leds]:DEVICE");
|
||||
help_abstract("Save and restore backlight brightness at shutdown and boot.");
|
||||
|
||||
help_section("Commands");
|
||||
r = table_print_or_warn(verbs);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
help_man_page_reference("systemd-backlight", "8");
|
||||
return 0;
|
||||
}
|
||||
COMMAND(
|
||||
"systemd-backlight\0",
|
||||
"Save and restore backlight brightness at shutdown and boot.",
|
||||
.argspec = "COMMAND [backlight|leds]:DEVICE\0",
|
||||
.man_pages = "systemd-backlight.8\0",
|
||||
);
|
||||
|
||||
static int has_multiple_graphics_cards(void) {
|
||||
_cleanup_(sd_device_enumerator_unrefp) sd_device_enumerator *e = NULL;
|
||||
@@ -639,15 +625,45 @@ static int verb_save(int argc, char *argv[], uintptr_t _data, void *userdata) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int run(int argc, char *argv[]) {
|
||||
log_setup();
|
||||
VERB_COMMON_HELP_AUTO_HIDDEN("systemd-backlight");
|
||||
|
||||
if (argv_looks_like_help(argc, argv))
|
||||
return help();
|
||||
static int parse_argv(int argc, char *argv[], char ***ret_args) {
|
||||
assert(argc >= 0);
|
||||
assert(argv);
|
||||
assert(ret_args);
|
||||
|
||||
OptionParser opts = { argc, argv };
|
||||
|
||||
FOREACH_OPTION_OR_RETURN(c, &opts)
|
||||
switch (c) {
|
||||
|
||||
OPTION_COMMON_HELP:
|
||||
return command_print_help("systemd-backlight");
|
||||
|
||||
OPTION_COMMON_VERSION:
|
||||
return version();
|
||||
|
||||
OPTION_COMMON_INTROSPECT_CLI:
|
||||
return introspect_cli(SD_JSON_FORMAT_OFF);
|
||||
}
|
||||
|
||||
*ret_args = option_parser_get_args(&opts);
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int run(int argc, char *argv[]) {
|
||||
int r;
|
||||
|
||||
log_setup();
|
||||
|
||||
umask(0022);
|
||||
|
||||
return dispatch_verb(strv_skip(argv, 1), /* userdata= */ NULL);
|
||||
char **args = NULL;
|
||||
r = parse_argv(argc, argv, &args);
|
||||
if (r <= 0)
|
||||
return r;
|
||||
|
||||
return dispatch_verb(args, /* userdata= */ NULL);
|
||||
}
|
||||
|
||||
DEFINE_MAIN_FUNCTION(run);
|
||||
|
||||
+27
-59
@@ -42,7 +42,6 @@
|
||||
#include "mountpoint-util.h"
|
||||
#include "namespace-util.h"
|
||||
#include "nsresource.h"
|
||||
#include "options.h"
|
||||
#include "parse-argument.h"
|
||||
#include "parse-util.h"
|
||||
#include "path-util.h"
|
||||
@@ -60,6 +59,7 @@
|
||||
#include "tmpfile-util.h"
|
||||
#include "uid-classification.h"
|
||||
#include "user-util.h"
|
||||
#include "verbs.h"
|
||||
#include "vpick.h"
|
||||
|
||||
static enum {
|
||||
@@ -121,63 +121,28 @@ STATIC_DESTRUCTOR_REGISTER(arg_loop_ref, freep);
|
||||
STATIC_DESTRUCTOR_REGISTER(arg_image_policy, image_policy_freep);
|
||||
STATIC_DESTRUCTOR_REGISTER(arg_image_filter, image_filter_freep);
|
||||
|
||||
static int help(void) {
|
||||
_cleanup_free_ char *link = NULL;
|
||||
_cleanup_(table_unrefp) Table *options = NULL, *commands = NULL;
|
||||
int r;
|
||||
|
||||
pager_open(arg_pager_flags);
|
||||
|
||||
r = terminal_urlify_man("systemd-dissect", "1", &link);
|
||||
if (r < 0)
|
||||
return log_oom();
|
||||
|
||||
r = option_parser_get_help_table_ns("systemd-dissect", &options);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
r = option_parser_get_help_table_full("systemd-dissect", "Commands", &commands);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
/* Make the 1st column same width in both tables */
|
||||
(void) table_sync_column_widths(0, options, commands);
|
||||
|
||||
printf("%1$s [OPTIONS...] IMAGE\n"
|
||||
"%1$s [OPTIONS...] --mount IMAGE PATH\n"
|
||||
"%1$s [OPTIONS...] --umount PATH\n"
|
||||
"%1$s [OPTIONS...] --attach IMAGE\n"
|
||||
"%1$s [OPTIONS...] --detach PATH\n"
|
||||
"%1$s [OPTIONS...] --list IMAGE\n"
|
||||
"%1$s [OPTIONS...] --mtree IMAGE\n"
|
||||
"%1$s [OPTIONS...] --with IMAGE [COMMAND…]\n"
|
||||
"%1$s [OPTIONS...] --copy-from IMAGE PATH [TARGET]\n"
|
||||
"%1$s [OPTIONS...] --copy-to IMAGE [SOURCE] PATH\n"
|
||||
"%1$s [OPTIONS...] --make-archive IMAGE [TARGET]\n"
|
||||
"%1$s [OPTIONS...] --discover\n"
|
||||
"%1$s [OPTIONS...] --validate IMAGE\n"
|
||||
"%1$s [OPTIONS...] --shift IMAGE UIDBASE\n"
|
||||
"\n%2$sDissect a Discoverable Disk Image (DDI).%3$s\n"
|
||||
"\n%4$sOptions:%5$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("\n%sCommands:%s\n", ansi_underline(), ansi_normal());
|
||||
|
||||
r = table_print_or_warn(commands);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
printf("\nSee the %s for details.\n", link);
|
||||
return 0;
|
||||
}
|
||||
COMMAND(
|
||||
"systemd-dissect\0",
|
||||
"Dissect a Discoverable Disk Image (DDI).",
|
||||
.argspec =
|
||||
"IMAGE\0"
|
||||
"--mount IMAGE PATH\0"
|
||||
"--umount PATH\0"
|
||||
"--attach IMAGE\0"
|
||||
"--detach PATH\0"
|
||||
"--list IMAGE\0"
|
||||
"--mtree IMAGE\0"
|
||||
"--with IMAGE [COMMAND…]\0"
|
||||
"--copy-from IMAGE PATH [TARGET]\0"
|
||||
"--copy-to IMAGE [SOURCE] PATH\0"
|
||||
"--make-archive IMAGE [TARGET]\0"
|
||||
"--discover\0"
|
||||
"--validate IMAGE\0"
|
||||
"--shift IMAGE UIDBASE\0",
|
||||
.man_pages = "systemd-dissect.1\0",
|
||||
.option_namespace = "systemd-dissect",
|
||||
.pager_flags = &arg_pager_flags,
|
||||
);
|
||||
|
||||
static int parse_image_path_argument(const char *path, char **ret_root, char **ret_image) {
|
||||
_cleanup_free_ char *p = NULL;
|
||||
@@ -439,7 +404,7 @@ static int parse_argv(int argc, char *argv[]) {
|
||||
OPTION_GROUP("Commands"): {}
|
||||
|
||||
OPTION_COMMON_HELP:
|
||||
return help();
|
||||
return command_print_help("systemd-dissect");
|
||||
|
||||
OPTION_COMMON_VERSION:
|
||||
return version();
|
||||
@@ -512,6 +477,9 @@ static int parse_argv(int argc, char *argv[]) {
|
||||
OPTION_LONG("shift", NULL, "Shift UID range to selected base"):
|
||||
arg_action = ACTION_SHIFT;
|
||||
break;
|
||||
|
||||
OPTION_COMMON_INTROSPECT_CLI:
|
||||
return introspect_cli(arg_json_format_flags);
|
||||
}
|
||||
|
||||
if (system_scope_requested || user_scope_requested)
|
||||
|
||||
+13
-86
@@ -32,7 +32,6 @@
|
||||
#include "fs-util.h"
|
||||
#include "glyph-util.h"
|
||||
#include "hashmap.h"
|
||||
#include "help-util.h"
|
||||
#include "hexdecoct.h"
|
||||
#include "home-util.h"
|
||||
#include "homectl-fido2.h"
|
||||
@@ -180,6 +179,14 @@ static int acquire_bus(sd_bus **bus) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
COMMAND(
|
||||
"homectl\0",
|
||||
"Create, manipulate or inspect home directories.",
|
||||
.man_pages = "homectl.1\0",
|
||||
.pager_flags = &arg_pager_flags,
|
||||
.flags = COMMAND_HELP_SEPARATE, /* the option table is very wide */
|
||||
);
|
||||
|
||||
VERB_GROUP("Basic User Manipulation Commands");
|
||||
VERB(verb_list_homes, "list", /* argspec= */ NULL, VERB_ANY, 1, VERB_DEFAULT,
|
||||
"List home areas");
|
||||
@@ -4002,90 +4009,7 @@ static int parse_fido2_device_field(const char *arg) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int help(void) {
|
||||
static const char* const vgroups[] = {
|
||||
"Basic User Manipulation Commands",
|
||||
"Advanced User Manipulation Commands",
|
||||
"User Migration Commands",
|
||||
"Signing Keys Commands",
|
||||
"Lock/Unlock Commands",
|
||||
"Other Commands",
|
||||
};
|
||||
|
||||
static const char* const ogroups[] = {
|
||||
NULL,
|
||||
"General User Record Properties",
|
||||
"Authentication User Record Properties",
|
||||
"Blob Directory User Record Properties",
|
||||
"Account Management User Record Properties",
|
||||
"Password Policy User Record Properties",
|
||||
"Resource Management User Record Properties",
|
||||
"Storage User Record Properties",
|
||||
"LUKS Storage User Record Properties",
|
||||
"Mounting User Record Properties",
|
||||
"CIFS User Record Properties",
|
||||
"Login Behaviour User Record Properties",
|
||||
};
|
||||
|
||||
Table *vtables[ELEMENTSOF(vgroups)] = {};
|
||||
CLEANUP_ELEMENTS(vtables, table_unref_array_clear);
|
||||
Table *otables[ELEMENTSOF(ogroups)] = {};
|
||||
CLEANUP_ELEMENTS(otables, table_unref_array_clear);
|
||||
int r;
|
||||
|
||||
for (size_t i = 0; i < ELEMENTSOF(vgroups); i++) {
|
||||
r = verbs_get_help_table_group(vgroups[i], &vtables[i]);
|
||||
if (r < 0)
|
||||
return r;
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < ELEMENTSOF(ogroups); i++) {
|
||||
r = option_parser_get_help_table_group(ogroups[i], &otables[i]);
|
||||
if (r < 0)
|
||||
return r;
|
||||
}
|
||||
|
||||
/* The two groups are not synchronized because the option table is very wide. */
|
||||
|
||||
assert_cc(ELEMENTSOF(vtables) == 6);
|
||||
(void) table_sync_column_widths(0, vtables[0], vtables[1], vtables[2],
|
||||
vtables[3], vtables[4], vtables[5]);
|
||||
|
||||
assert_cc(ELEMENTSOF(otables) == 12);
|
||||
(void) table_sync_column_widths(0, otables[0], otables[1], otables[2], otables[3],
|
||||
otables[4], otables[5], otables[6], otables[7],
|
||||
otables[8], otables[9], otables[10], otables[11]);
|
||||
|
||||
pager_open(arg_pager_flags);
|
||||
|
||||
help_cmdline("[OPTIONS…] COMMAND …");
|
||||
help_abstract("Create, manipulate or inspect home directories.");
|
||||
|
||||
for (size_t i = 0; i < ELEMENTSOF(vgroups); i++) {
|
||||
help_section(vgroups[i]);
|
||||
r = table_print_or_warn(vtables[i]);
|
||||
if (r < 0)
|
||||
return r;
|
||||
}
|
||||
|
||||
help_section("Options");
|
||||
r = table_print_or_warn(otables[0]);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
for (size_t i = 1; i < ELEMENTSOF(ogroups); i++) {
|
||||
help_section(ogroups[i]);
|
||||
r = table_print_or_warn(otables[i]);
|
||||
if (r < 0)
|
||||
return r;
|
||||
}
|
||||
|
||||
help_man_page_reference("homectl", "1");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
VERB_COMMON_HELP_HIDDEN(help);
|
||||
VERB_COMMON_HELP_AUTO_HIDDEN("homectl");
|
||||
|
||||
static int parse_argv(int argc, char *argv[], char ***remaining_args) {
|
||||
_cleanup_strv_free_ char **arg_languages = NULL;
|
||||
@@ -4114,7 +4038,7 @@ static int parse_argv(int argc, char *argv[], char ***remaining_args) {
|
||||
switch (c) {
|
||||
|
||||
OPTION_COMMON_HELP:
|
||||
return help();
|
||||
return command_print_help("homectl");
|
||||
|
||||
OPTION_COMMON_VERSION:
|
||||
return version();
|
||||
@@ -4929,6 +4853,9 @@ static int parse_argv(int argc, char *argv[], char ***remaining_args) {
|
||||
OPTION_SHORT('N', NULL, /* help= */ NULL):
|
||||
match_identity = &arg_identity_extra_other_machines;
|
||||
break;
|
||||
|
||||
OPTION_COMMON_INTROSPECT_CLI:
|
||||
return introspect_cli(arg_json_format_flags);
|
||||
}
|
||||
|
||||
if (!strv_isempty(arg_languages)) {
|
||||
|
||||
+12
-42
@@ -14,7 +14,6 @@
|
||||
#include "main-func.h"
|
||||
#include "options.h"
|
||||
#include "parse-argument.h"
|
||||
#include "pretty-print.h"
|
||||
#include "string-util.h"
|
||||
#include "strv.h"
|
||||
#include "verbs.h"
|
||||
@@ -26,6 +25,13 @@ static PagerFlags arg_pager_flags = 0;
|
||||
static bool arg_legend = true;
|
||||
static sd_json_format_flags_t arg_json_format_flags = SD_JSON_FORMAT_OFF;
|
||||
|
||||
COMMAND(
|
||||
"systemd-id128\0",
|
||||
"Generate and print 128-bit identifiers.",
|
||||
.man_pages = "systemd-id128.1\0",
|
||||
.pager_flags = &arg_pager_flags,
|
||||
);
|
||||
|
||||
static int print_id(sd_id128_t id) {
|
||||
_cleanup_(sd_json_variant_unrefp) sd_json_variant *json = NULL;
|
||||
int r;
|
||||
@@ -228,46 +234,7 @@ static int verb_show(int argc, char *argv[], uintptr_t _data, void *userdata) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int help(void) {
|
||||
_cleanup_free_ char *link = NULL;
|
||||
_cleanup_(table_unrefp) Table *options = NULL, *verbs = NULL;
|
||||
int r;
|
||||
|
||||
r = terminal_urlify_man("systemd-id128", "1", &link);
|
||||
if (r < 0)
|
||||
return log_oom();
|
||||
|
||||
r = option_parser_get_help_table(&options);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
r = verbs_get_help_table(&verbs);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
/* Make the 1st column same width in both tables */
|
||||
(void) table_sync_column_widths(0, options, verbs);
|
||||
|
||||
printf("%s [OPTIONS...] COMMAND\n\n"
|
||||
"%sGenerate and print 128-bit identifiers.%s\n"
|
||||
"\nCommands:\n",
|
||||
program_invocation_short_name,
|
||||
ansi_highlight(),
|
||||
ansi_normal());
|
||||
r = table_print_or_warn(verbs);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
printf("\nOptions:\n");
|
||||
r = table_print_or_warn(options);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
printf("\nSee the %s for details.\n", link);
|
||||
return 0;
|
||||
}
|
||||
|
||||
VERB_COMMON_HELP(help);
|
||||
VERB_COMMON_HELP_AUTO("systemd-id128");
|
||||
|
||||
static int parse_argv(int argc, char *argv[], char ***ret_args) {
|
||||
int r;
|
||||
@@ -280,7 +247,7 @@ static int parse_argv(int argc, char *argv[], char ***ret_args) {
|
||||
FOREACH_OPTION_OR_RETURN(c, &opts)
|
||||
switch (c) {
|
||||
OPTION_COMMON_HELP:
|
||||
return help();
|
||||
return command_print_help("systemd-id128");
|
||||
|
||||
OPTION_COMMON_VERSION:
|
||||
return version();
|
||||
@@ -326,6 +293,9 @@ static int parse_argv(int argc, char *argv[], char ***ret_args) {
|
||||
OPTION('u', "uuid", NULL, "Output in UUID format"):
|
||||
arg_mode = ID128_PRINT_UUID;
|
||||
break;
|
||||
|
||||
OPTION_COMMON_INTROSPECT_CLI:
|
||||
return introspect_cli(arg_json_format_flags);
|
||||
}
|
||||
|
||||
if (arg_mode == ID128_PRINT_PRETTY && sd_json_format_enabled(arg_json_format_flags))
|
||||
|
||||
+20
-35
@@ -6,6 +6,7 @@
|
||||
|
||||
#include "sd-daemon.h"
|
||||
#include "sd-event.h"
|
||||
#include "sd-json.h"
|
||||
|
||||
#include "alloc-util.h"
|
||||
#include "build.h"
|
||||
@@ -15,13 +16,10 @@
|
||||
#include "exit-status.h"
|
||||
#include "fd-util.h"
|
||||
#include "fdset.h"
|
||||
#include "format-table.h"
|
||||
#include "format-util.h"
|
||||
#include "help-util.h"
|
||||
#include "log.h"
|
||||
#include "main-func.h"
|
||||
#include "notify-recv.h"
|
||||
#include "options.h"
|
||||
#include "parse-util.h"
|
||||
#include "pidref.h"
|
||||
#include "process-util.h"
|
||||
@@ -30,6 +28,7 @@
|
||||
#include "strv.h"
|
||||
#include "time-util.h"
|
||||
#include "user-util.h"
|
||||
#include "verbs.h"
|
||||
|
||||
static enum {
|
||||
ACTION_NOTIFY,
|
||||
@@ -56,27 +55,15 @@ STATIC_DESTRUCTOR_REGISTER(arg_exec, strv_freep);
|
||||
STATIC_DESTRUCTOR_REGISTER(arg_fds, fdset_freep);
|
||||
STATIC_DESTRUCTOR_REGISTER(arg_fdname, freep);
|
||||
|
||||
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...] [VARIABLE=VALUE...]");
|
||||
help_cmdline("[OPTIONS...] --exec [VARIABLE=VALUE...] ; -- CMDLINE...");
|
||||
help_cmdline("[OPTIONS...] --fork -- CMDLINE...");
|
||||
help_abstract("Notify the service manager about service status updates.");
|
||||
|
||||
help_section("Options");
|
||||
r = table_print_or_warn(options);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
help_man_page_reference("systemd-notify", "1");
|
||||
return 0;
|
||||
}
|
||||
COMMAND(
|
||||
"systemd-notify\0",
|
||||
"Notify the service manager about service status updates.",
|
||||
.argspec =
|
||||
"[VARIABLE=VALUE…]\0"
|
||||
"--exec [VARIABLE=VALUE…] ; -- CMDLINE…\0"
|
||||
"--fork -- CMDLINE…\0",
|
||||
.man_pages = "systemd-notify.1\0",
|
||||
);
|
||||
|
||||
static int get_manager_pid(PidRef *ret) {
|
||||
int r;
|
||||
@@ -159,7 +146,7 @@ static int parse_argv(int argc, char *argv[], char ***ret_args) {
|
||||
switch (c) {
|
||||
|
||||
OPTION_COMMON_HELP:
|
||||
return help();
|
||||
return command_print_help("systemd-notify");
|
||||
|
||||
OPTION_COMMON_VERSION:
|
||||
return version();
|
||||
@@ -273,6 +260,9 @@ static int parse_argv(int argc, char *argv[], char ***ret_args) {
|
||||
OPTION('q', "quiet", NULL, "Do not show PID of child when forking"):
|
||||
arg_quiet = true;
|
||||
break;
|
||||
|
||||
OPTION_COMMON_INTROSPECT_CLI:
|
||||
return introspect_cli(SD_JSON_FORMAT_OFF);
|
||||
}
|
||||
|
||||
bool have_env = arg_ready || arg_stopping || arg_reloading || arg_status || pidref_is_set(&arg_pid) || !fdset_isempty(arg_fds);
|
||||
@@ -283,7 +273,7 @@ static int parse_argv(int argc, char *argv[], char ***ret_args) {
|
||||
|
||||
case ACTION_NOTIFY: {
|
||||
if (arg_fdname && fdset_isempty(arg_fds))
|
||||
return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "No file descriptors passed, but --fdname= set, refusing.");
|
||||
return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "No file descriptors passed, but --fdname= used, refusing.");
|
||||
|
||||
size_t n_arg_env;
|
||||
|
||||
@@ -308,14 +298,10 @@ static int parse_argv(int argc, char *argv[], char ***ret_args) {
|
||||
n_arg_env = strv_length(args);
|
||||
|
||||
have_env = have_env || n_arg_env > 0;
|
||||
if (!have_env) {
|
||||
if (do_exec)
|
||||
return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "No notify message specified while --exec, refusing.");
|
||||
|
||||
/* No argument at all? */
|
||||
help();
|
||||
return -EINVAL;
|
||||
}
|
||||
if (!have_env)
|
||||
return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
|
||||
do_exec ? "No notify message specified with --exec, refusing." :
|
||||
"Nothing to notify about was specified.");
|
||||
|
||||
if (n_arg_env > 0) {
|
||||
arg_env = strv_copy_n(args, n_arg_env);
|
||||
@@ -417,7 +403,6 @@ static int on_child(sd_event_source *s, const siginfo_t *si, void *userdata) {
|
||||
}
|
||||
|
||||
static int action_fork(char *const *_command) {
|
||||
|
||||
static const int forward_signals[] = {
|
||||
SIGHUP,
|
||||
SIGTERM,
|
||||
|
||||
@@ -2,18 +2,18 @@
|
||||
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "sd-json.h"
|
||||
|
||||
#include "alloc-util.h"
|
||||
#include "build.h"
|
||||
#include "extract-word.h"
|
||||
#include "fileio.h"
|
||||
#include "format-table.h"
|
||||
#include "help-util.h"
|
||||
#include "log.h"
|
||||
#include "options.h"
|
||||
#include "resolvconf-compat.h"
|
||||
#include "resolvectl.h"
|
||||
#include "string-util.h"
|
||||
#include "strv.h"
|
||||
#include "verbs.h"
|
||||
|
||||
typedef enum LookupType {
|
||||
LOOKUP_TYPE_REGULAR,
|
||||
@@ -21,35 +21,22 @@ typedef enum LookupType {
|
||||
LOOKUP_TYPE_EXCLUSIVE, /* -x */
|
||||
} LookupType;
|
||||
|
||||
static int resolvconf_help(void) {
|
||||
_cleanup_(table_unrefp) Table *options = NULL;
|
||||
int r;
|
||||
|
||||
r = option_parser_get_help_table_ns("resolvconf", &options);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
help_cmdline("-a INTERFACE <FILE");
|
||||
help_cmdline("-d INTERFACE");
|
||||
help_abstract("Register DNS server and domain configuration with systemd-resolved.");
|
||||
|
||||
help_section("Options");
|
||||
r = table_print_or_warn(options);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
printf("\n"
|
||||
"This is a compatibility alias for the resolvectl(1) tool, providing native\n"
|
||||
"command line compatibility with the resolvconf(8) tool of various Linux\n"
|
||||
"distributions and BSD systems. Some options supported by other implementations\n"
|
||||
"are not supported and are ignored: -m, -u. Various options supported by other\n"
|
||||
"implementations are not supported and will cause the invocation to fail:\n"
|
||||
"-I, -i, -l, -R, -r, -v, -V, --enable-updates, --disable-updates,\n"
|
||||
"--updates-are-enabled.\n");
|
||||
|
||||
help_man_page_reference("resolvectl", "1");
|
||||
return 0;
|
||||
}
|
||||
COMMAND(
|
||||
"resolvconf\0",
|
||||
"Register DNS server and domain configuration with systemd-resolved.",
|
||||
.man_pages = "resolvectl.1\0",
|
||||
.argspec = "-a INTERFACE <FILE\0"
|
||||
"-d INTERFACE\0",
|
||||
.footer =
|
||||
"This is a compatibility alias for the resolvectl(1) tool, providing native "
|
||||
"command line compatibility with the resolvconf(8) tool of various Linux "
|
||||
"distributions and BSD systems. Some options supported by other implementations "
|
||||
"are not supported and are ignored: -m, -u. Various options supported by other "
|
||||
"implementations are not supported and will cause the invocation to fail: "
|
||||
"-I, -i, -l, -R, -r, -v, -V, --enable-updates, --disable-updates, "
|
||||
"--updates-are-enabled.",
|
||||
.option_namespace = "resolvconf",
|
||||
);
|
||||
|
||||
static int parse_nameserver(const char *string) {
|
||||
int r;
|
||||
@@ -194,7 +181,7 @@ int resolvconf_parse_argv(int argc, char *argv[]) {
|
||||
OPTION_NAMESPACE("resolvconf"): {}
|
||||
|
||||
OPTION_COMMON_HELP:
|
||||
return resolvconf_help();
|
||||
return command_print_help("resolvconf");
|
||||
|
||||
OPTION_COMMON_VERSION:
|
||||
return version();
|
||||
@@ -253,6 +240,9 @@ int resolvconf_parse_argv(int argc, char *argv[]) {
|
||||
OPTION_LONG("updates-are-enabled", NULL, /* help= */ NULL):
|
||||
return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
|
||||
"Switch --updates-are-enabled not supported.");
|
||||
|
||||
OPTION_COMMON_INTROSPECT_CLI:
|
||||
return introspect_cli(SD_JSON_FORMAT_OFF);
|
||||
}
|
||||
|
||||
if (arg_mode == _MODE_INVALID)
|
||||
|
||||
+26
-63
@@ -30,7 +30,6 @@
|
||||
#include "format-ifname.h"
|
||||
#include "format-table.h"
|
||||
#include "glyph-util.h"
|
||||
#include "help-util.h"
|
||||
#include "hostname-util.h"
|
||||
#include "json-util.h"
|
||||
#include "main-func.h"
|
||||
@@ -97,6 +96,15 @@ STATIC_DESTRUCTOR_REGISTER(arg_set_dns, strv_freep);
|
||||
STATIC_DESTRUCTOR_REGISTER(arg_set_domain, strv_freep);
|
||||
STATIC_DESTRUCTOR_REGISTER(arg_set_nta, strv_freep);
|
||||
|
||||
COMMAND(
|
||||
"resolvectl\0",
|
||||
"Send control commands to the network name resolution manager, or "
|
||||
"resolve domain names, IPv4 and IPv6 addresses, DNS records, and services.",
|
||||
.man_pages = "resolvectl.1\0",
|
||||
.option_namespace = "resolvectl",
|
||||
.pager_flags = &arg_pager_flags,
|
||||
);
|
||||
|
||||
typedef enum StatusMode {
|
||||
STATUS_ALL,
|
||||
STATUS_DNS,
|
||||
@@ -3224,67 +3232,15 @@ static void help_dns_classes(void) {
|
||||
DUMP_STRING_TABLE(dns_class, int, _DNS_CLASS_MAX);
|
||||
}
|
||||
|
||||
static int compat_help(void) {
|
||||
_cleanup_(table_unrefp) Table *options = NULL;
|
||||
int r;
|
||||
VERB_COMMON_HELP_AUTO_HIDDEN("resolvectl");
|
||||
|
||||
r = option_parser_get_help_table_ns("systemd-resolve", &options);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
pager_open(arg_pager_flags);
|
||||
|
||||
help_cmdline("[OPTIONS…] HOSTNAME|ADDRESS…");
|
||||
help_cmdline("[OPTIONS…] --service [[NAME] TYPE] DOMAIN");
|
||||
help_cmdline("[OPTIONS…] --openpgp EMAIL@DOMAIN…");
|
||||
help_cmdline("[OPTIONS…] --statistics");
|
||||
help_cmdline("[OPTIONS…] --reset-statistics");
|
||||
help_abstract("Resolve domain names, IPv4 and IPv6 addresses, DNS records, and services.");
|
||||
|
||||
help_section("Options");
|
||||
r = table_print_or_warn(options);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
help_man_page_reference("resolvectl", "1");
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int native_help(void) {
|
||||
_cleanup_(table_unrefp) Table *verbs = NULL, *options = NULL;
|
||||
int r;
|
||||
|
||||
r = verbs_get_help_table(&verbs);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
r = option_parser_get_help_table_ns("resolvectl", &options);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
(void) table_sync_column_widths(0, verbs, options);
|
||||
|
||||
pager_open(arg_pager_flags);
|
||||
|
||||
help_cmdline("[OPTIONS…] COMMAND …");
|
||||
help_abstract("Send control commands to the network name resolution manager, or\n"
|
||||
"resolve domain names, IPv4 and IPv6 addresses, DNS records, and services.");
|
||||
|
||||
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("resolvectl", "1");
|
||||
return 0;
|
||||
}
|
||||
|
||||
VERB_COMMON_HELP_HIDDEN(native_help);
|
||||
COMMAND(
|
||||
"systemd-resolve\0",
|
||||
"This command is deprecated. Use resolvectl.1 instead.",
|
||||
.man_pages = "resolvectl.1\0",
|
||||
.option_namespace = "systemd-resolve",
|
||||
.pager_flags = &arg_pager_flags,
|
||||
);
|
||||
|
||||
static int compat_parse_argv(int argc, char *argv[], char ***remaining_args) {
|
||||
int r;
|
||||
@@ -3301,7 +3257,8 @@ static int compat_parse_argv(int argc, char *argv[], char ***remaining_args) {
|
||||
OPTION_NAMESPACE("systemd-resolve"): {}
|
||||
|
||||
OPTION_COMMON_HELP:
|
||||
return compat_help();
|
||||
printf("systemd-resolve is deprecated. Call resolvectl instead.\n");
|
||||
return 0;
|
||||
|
||||
OPTION_COMMON_VERSION:
|
||||
return version();
|
||||
@@ -3499,6 +3456,9 @@ static int compat_parse_argv(int argc, char *argv[], char ***remaining_args) {
|
||||
if (r < 0)
|
||||
return r;
|
||||
break;
|
||||
|
||||
OPTION_COMMON_INTROSPECT_CLI:
|
||||
return introspect_cli(arg_json_format_flags);
|
||||
}
|
||||
|
||||
if (arg_type == 0 && arg_class != 0)
|
||||
@@ -3541,7 +3501,7 @@ static int native_parse_argv(int argc, char *argv[], char ***remaining_args) {
|
||||
OPTION_NAMESPACE("resolvectl"): {}
|
||||
|
||||
OPTION_COMMON_HELP:
|
||||
return native_help();
|
||||
return command_print_help("resolvectl");
|
||||
|
||||
OPTION_COMMON_VERSION:
|
||||
return version();
|
||||
@@ -3726,6 +3686,9 @@ static int native_parse_argv(int argc, char *argv[], char ***remaining_args) {
|
||||
OPTION_COMMON_LOWERCASE_J:
|
||||
arg_json_format_flags = SD_JSON_FORMAT_PRETTY_AUTO|SD_JSON_FORMAT_COLOR_AUTO;
|
||||
break;
|
||||
|
||||
OPTION_COMMON_INTROSPECT_CLI:
|
||||
return introspect_cli(arg_json_format_flags);
|
||||
}
|
||||
|
||||
if (arg_raw != RAW_NONE && sd_json_format_enabled(arg_json_format_flags))
|
||||
|
||||
@@ -2250,6 +2250,29 @@ int _table_sync_column_widths(size_t column, Table *a, ...) {
|
||||
return r;
|
||||
}
|
||||
|
||||
int table_sync_all_column_widths(size_t column, Table **a) {
|
||||
size_t max = 0;
|
||||
int r;
|
||||
|
||||
/* Make the specified column have the same width in the tables. */
|
||||
|
||||
for (Table **t = a; t && *t; t++) {
|
||||
size_t w;
|
||||
|
||||
r = table_data_requested_width(*t, column, &w);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to query table column width: %m");
|
||||
|
||||
max = MAX(max, w);
|
||||
}
|
||||
|
||||
r = 0;
|
||||
for (Table **t = a; t && *t; t++)
|
||||
RET_GATHER(r, table_set_column_width(*t, column, max));
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
int table_print_full(Table *t, FILE *f, bool flush) {
|
||||
_cleanup_free_ size_t *sorted = NULL;
|
||||
int r;
|
||||
|
||||
@@ -149,6 +149,7 @@ int table_data_requested_width(Table *table, size_t column, size_t *ret);
|
||||
int table_set_column_width(Table *t, size_t column, size_t width);
|
||||
int _table_sync_column_widths(size_t column, Table *a, ...);
|
||||
#define table_sync_column_widths(column, a, ...) _table_sync_column_widths(column, a, __VA_ARGS__, NULL)
|
||||
int table_sync_all_column_widths(size_t column, Table **a);
|
||||
|
||||
int table_print_full(Table *t, FILE *f, bool flush);
|
||||
static inline int table_print(Table *t) {
|
||||
|
||||
@@ -85,6 +85,7 @@ typedef struct MachineCredentialContext MachineCredentialContext;
|
||||
typedef struct MountOptions MountOptions;
|
||||
typedef struct MStack MStack;
|
||||
typedef struct OpenFile OpenFile;
|
||||
typedef struct Option Option;
|
||||
typedef struct Pkcs11EncryptedKey Pkcs11EncryptedKey;
|
||||
typedef struct QmpClient QmpClient;
|
||||
typedef struct QmpSlot QmpSlot;
|
||||
@@ -94,4 +95,5 @@ typedef struct Tpm2Handle Tpm2Handle;
|
||||
typedef struct Tpm2PCRValue Tpm2PCRValue;
|
||||
typedef struct UnitInfo UnitInfo;
|
||||
typedef struct UserRecord UserRecord;
|
||||
typedef struct Verb Verb;
|
||||
typedef struct VeritySettings VeritySettings;
|
||||
|
||||
+184
-1
@@ -1,5 +1,7 @@
|
||||
/* SPDX-License-Identifier: LGPL-2.1-or-later */
|
||||
|
||||
#include "sd-json.h"
|
||||
|
||||
#include "format-table.h"
|
||||
#include "log.h"
|
||||
#include "options.h"
|
||||
@@ -470,6 +472,24 @@ char* option_get_synopsis(const Option *opt, const char *joiner, bool show_metav
|
||||
option_arg_optional(opt) ? "]" : "");
|
||||
}
|
||||
|
||||
const Option* options_find_namespace(
|
||||
const Option options[],
|
||||
const Option options_end[],
|
||||
const char *namespace) {
|
||||
|
||||
if (!namespace)
|
||||
/* The first part is the default unnamed namespace, so
|
||||
* if the namespace was not specified, we are in it. */
|
||||
return options;
|
||||
|
||||
for (const Option *opt = options; opt < options_end; opt++)
|
||||
if (FLAGS_SET(opt->flags, OPTION_NAMESPACE_MARKER) &&
|
||||
streq(namespace, ASSERT_PTR(opt->long_code)))
|
||||
return opt + 1;
|
||||
|
||||
return NULL; /* not found :/ */
|
||||
}
|
||||
|
||||
int _option_parser_get_help_table_full(
|
||||
const Option options[],
|
||||
const Option options_end[],
|
||||
@@ -478,6 +498,8 @@ int _option_parser_get_help_table_full(
|
||||
Table **ret) {
|
||||
int r;
|
||||
|
||||
// TODO: drop this function and all its wrappers
|
||||
|
||||
assert(ret);
|
||||
|
||||
_cleanup_(table_unrefp) Table *table = table_new("names", "help");
|
||||
@@ -492,7 +514,8 @@ int _option_parser_get_help_table_full(
|
||||
* <group>? The first part is the default group, so if the group was
|
||||
* not specified, we are in it. */
|
||||
|
||||
for (const Option *opt = options; opt < options_end; opt++) {
|
||||
const Option *opt;
|
||||
for (opt = options; opt < options_end; opt++) {
|
||||
bool ns_marker = FLAGS_SET(opt->flags, OPTION_NAMESPACE_MARKER);
|
||||
if (!in_ns) {
|
||||
in_ns = ns_marker && streq(namespace, opt->long_code);
|
||||
@@ -543,5 +566,165 @@ int _option_parser_get_help_table_full(
|
||||
|
||||
table_set_header(table, false);
|
||||
*ret = TAKE_PTR(table);
|
||||
assert(opt - options < INT_MAX);
|
||||
return opt - options;
|
||||
}
|
||||
|
||||
int options_get_help_table_group(
|
||||
const Option options[],
|
||||
const Option options_end[],
|
||||
Table **ret,
|
||||
const char **ret_group) {
|
||||
int r;
|
||||
|
||||
assert(ret);
|
||||
assert(ret_group);
|
||||
|
||||
_cleanup_(table_unrefp) Table *table = table_new("names", "help");
|
||||
if (!table)
|
||||
return log_oom();
|
||||
|
||||
assert(options_end > options);
|
||||
|
||||
bool group_marker = FLAGS_SET(options[0].flags, OPTION_GROUP_MARKER);
|
||||
const char *group = group_marker ? ASSERT_PTR(options[0].long_code) : NULL;
|
||||
|
||||
const Option *opt;
|
||||
for (opt = options + group_marker; opt < options_end; opt++) {
|
||||
if (FLAGS_SET(opt->flags, OPTION_NAMESPACE_MARKER))
|
||||
/* End of our namespace */
|
||||
break;
|
||||
|
||||
if (FLAGS_SET(opt->flags, OPTION_GROUP_MARKER))
|
||||
/* End of the group */
|
||||
break;
|
||||
|
||||
if (!opt->help)
|
||||
/* No help string — we do not show the option */
|
||||
continue;
|
||||
|
||||
_cleanup_free_ char *s = option_get_synopsis(opt, " ", /* show_metavar= */ true);
|
||||
if (!s)
|
||||
return log_oom();
|
||||
|
||||
/* We indent the option string by two spaces. We could set the minimum cell width and
|
||||
* right-align for a similar result, but that'd be more work. This is only used for
|
||||
* display. */
|
||||
bool shift_left = opt->short_code != 0 || FLAGS_SET(opt->flags, OPTION_HELP_ENTRY_VERBATIM);
|
||||
const char *prefix = shift_left ? " " : " ";
|
||||
_cleanup_free_ char *t = strjoin(prefix, s);
|
||||
if (!t)
|
||||
return log_oom();
|
||||
|
||||
r = table_add_many(table, TABLE_STRING, t);
|
||||
if (r < 0)
|
||||
return table_log_add_error(r);
|
||||
|
||||
_cleanup_strv_free_ char **split = strv_split(opt->help, /* separators= */ NULL);
|
||||
if (!split)
|
||||
return log_oom();
|
||||
|
||||
r = table_add_many(table, TABLE_STRV_WRAPPED, split);
|
||||
if (r < 0)
|
||||
return table_log_add_error(r);
|
||||
}
|
||||
|
||||
assert(!table_isempty(table)); /* Empty group? Something is off. */
|
||||
|
||||
table_set_header(table, false);
|
||||
*ret = TAKE_PTR(table);
|
||||
*ret_group = group;
|
||||
|
||||
assert(opt - options < INT_MAX);
|
||||
return opt - options;
|
||||
}
|
||||
|
||||
static int option_build_json(const Option *opt, const char *group, sd_json_variant **ret) {
|
||||
_cleanup_(sd_json_variant_unrefp) sd_json_variant *names = NULL;
|
||||
int r;
|
||||
|
||||
assert(opt);
|
||||
assert(opt->short_code != 0 || opt->long_code);
|
||||
assert(ret);
|
||||
|
||||
if (opt->short_code != 0) {
|
||||
char s[3] = { '-', opt->short_code };
|
||||
|
||||
r = sd_json_variant_append_arrayb(&names, SD_JSON_BUILD_STRING(s));
|
||||
if (r < 0)
|
||||
return r;
|
||||
}
|
||||
|
||||
if (opt->long_code) {
|
||||
_cleanup_free_ char *s = strjoin("--", opt->long_code);
|
||||
if (!s)
|
||||
return -ENOMEM;
|
||||
|
||||
r = sd_json_variant_append_arrayb(&names, SD_JSON_BUILD_STRING(s));
|
||||
if (r < 0)
|
||||
return r;
|
||||
}
|
||||
|
||||
const char *argtype =
|
||||
option_arg_required(opt) ? "required_argument" :
|
||||
option_arg_optional(opt) ? "optional_argument" :
|
||||
"no_argument";
|
||||
|
||||
return sd_json_buildo(
|
||||
ret,
|
||||
SD_JSON_BUILD_PAIR_VARIANT("names", names),
|
||||
SD_JSON_BUILD_PAIR_STRING("argument", argtype),
|
||||
SD_JSON_BUILD_PAIR_CONDITION(
|
||||
!!opt->metavar,
|
||||
"metavar", SD_JSON_BUILD_STRING(opt->metavar)),
|
||||
SD_JSON_BUILD_PAIR_CONDITION(!!opt->help, "help", SD_JSON_BUILD_STRING(opt->help)),
|
||||
SD_JSON_BUILD_PAIR_CONDITION(
|
||||
!!group,
|
||||
"group", SD_JSON_BUILD_STRV(STRV_MAKE(group))));
|
||||
}
|
||||
|
||||
int options_build_json(
|
||||
const Option options[],
|
||||
const Option options_end[],
|
||||
const char *namespace,
|
||||
sd_json_variant **ret) {
|
||||
|
||||
_cleanup_(sd_json_variant_unrefp) sd_json_variant *array = NULL;
|
||||
const char *group = NULL;
|
||||
int r;
|
||||
|
||||
assert(ret);
|
||||
|
||||
const Option *start = options_find_namespace(options, options_end, namespace);
|
||||
if (!start)
|
||||
return log_error_errno(SYNTHETIC_ERRNO(EUCLEAN),
|
||||
"Option namespace %s not found.",
|
||||
namespace ?: "(unnamed)");
|
||||
|
||||
for (const Option *opt = start; opt < options_end; opt++) {
|
||||
if (FLAGS_SET(opt->flags, OPTION_NAMESPACE_MARKER))
|
||||
break; /* End of our namespace */
|
||||
|
||||
if (FLAGS_SET(opt->flags, OPTION_GROUP_MARKER)) {
|
||||
group = opt->long_code;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (option_is_metadata(opt))
|
||||
/* Positional and help entries are only used for display */
|
||||
continue;
|
||||
|
||||
_cleanup_(sd_json_variant_unrefp) sd_json_variant *o = NULL;
|
||||
r = option_build_json(opt, group, &o);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to build JSON object for option '%s': %m",
|
||||
opt->long_code ?: CHAR_TO_STR(opt->short_code));
|
||||
|
||||
r = sd_json_variant_append_array(&array, o);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to append JSON object to array: %m");
|
||||
}
|
||||
|
||||
*ret = TAKE_PTR(array);
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -95,6 +95,14 @@ assert_cc(sizeof(Option) % sizeof(void*) == 0);
|
||||
#define OPTION_COMMON_VERSION \
|
||||
OPTION_LONG("version", NULL, "Show package version")
|
||||
|
||||
#define OPTION_COMMON_INTROSPECT_CLI \
|
||||
/* This option is internal-only for now and not shown in --help */ \
|
||||
OPTION_LONG("introspect-cli", NULL, /* help= */ NULL)
|
||||
|
||||
/* For programs which do not do option parsing, but check the argv array directly. */
|
||||
#define OPTION_COMMON_INTROSPECT_CLI_ENTRY \
|
||||
_OPTION_ENTRY(__COUNTER__, /* fl= */ 0, /* sc= */ 0, "introspect-cli", /* mv= */ NULL, /* d= */ 0u, /* h= */ NULL)
|
||||
|
||||
#define OPTION_COMMON_NO_PAGER \
|
||||
OPTION_LONG("no-pager", NULL, "Do not start a pager")
|
||||
|
||||
@@ -266,6 +274,11 @@ char* option_parser_get_arg(const OptionParser *state, size_t i);
|
||||
|
||||
char* option_get_synopsis(const Option *opt, const char *joiner, bool show_metavar);
|
||||
|
||||
const Option* options_find_namespace(
|
||||
const Option options[],
|
||||
const Option options_end[],
|
||||
const char *namespace);
|
||||
|
||||
int _option_parser_get_help_table_full(
|
||||
const Option options[],
|
||||
const Option options_end[],
|
||||
@@ -280,3 +293,17 @@ int _option_parser_get_help_table_full(
|
||||
option_parser_get_help_table_full(/* namespace= */ NULL, group, ret)
|
||||
#define option_parser_get_help_table(ret) \
|
||||
option_parser_get_help_table_group(/* group= */ NULL, ret)
|
||||
|
||||
int options_get_help_table_group(
|
||||
const Option options[],
|
||||
const Option options_end[],
|
||||
Table **ret,
|
||||
const char **ret_group);
|
||||
|
||||
/* Build a JSON array of CLI-introspection option objects for the options in the given namespace.
|
||||
* Returns NULL in *ret if the namespace defines no options. */
|
||||
int options_build_json(
|
||||
const Option options[],
|
||||
const Option options_end[],
|
||||
const char *namespace,
|
||||
sd_json_variant **ret);
|
||||
|
||||
@@ -180,8 +180,13 @@ int terminal_urlify_path(const char *path, const char *text, char **ret) {
|
||||
int terminal_urlify_man_full(const char *page, const char *section, const char *suffix, char **ret) {
|
||||
const char *url, *text;
|
||||
|
||||
url = strjoina("man:", page, "(", section, ")");
|
||||
text = strjoina(page, "(", section, ")", suffix);
|
||||
if (section) {
|
||||
url = strjoina("man:", page, "(", section, ")");
|
||||
text = strjoina(page, "(", section, ")", suffix);
|
||||
} else {
|
||||
url = strjoina("man:", page);
|
||||
text = strjoina(page, suffix);
|
||||
}
|
||||
|
||||
return terminal_urlify(url, text, ret);
|
||||
}
|
||||
|
||||
+420
-5
@@ -1,8 +1,17 @@
|
||||
/* SPDX-License-Identifier: LGPL-2.1-or-later */
|
||||
|
||||
#include "sd-json.h"
|
||||
|
||||
#include "build.h"
|
||||
#include "env-util.h"
|
||||
#include "extract-word.h"
|
||||
#include "format-table.h"
|
||||
#include "help-util.h"
|
||||
#include "log.h"
|
||||
#include "nulstr-util.h"
|
||||
#include "options.h"
|
||||
#include "pager.h"
|
||||
#include "pretty-print.h"
|
||||
#include "string-util.h"
|
||||
#include "strv.h"
|
||||
#include "terminal-util.h"
|
||||
@@ -58,15 +67,16 @@ bool should_bypass(const char *env_prefix) {
|
||||
}
|
||||
|
||||
static bool verb_is_metadata(const Verb *verb) {
|
||||
/* A metadata entry that is not a real verb, like the group marker */
|
||||
return FLAGS_SET(ASSERT_PTR(verb)->flags, VERB_GROUP_MARKER);
|
||||
/* A metadata entry that is not a real verb, i.e. either the command and group markers. */
|
||||
return FLAGS_SET(ASSERT_PTR(verb)->flags, VERB_COMMAND_MARKER) ||
|
||||
FLAGS_SET(ASSERT_PTR(verb)->flags, VERB_GROUP_MARKER);
|
||||
}
|
||||
|
||||
const Verb* verbs_find_verb(const char *name, const Verb verbs[], const Verb verbs_end[]) {
|
||||
assert(verbs);
|
||||
assert(verbs_end > verbs);
|
||||
assert((uintptr_t) verbs % sizeof(void*) == 0);
|
||||
assert(verbs[0].verb);
|
||||
assert(FLAGS_SET(verbs[0].flags, VERB_COMMAND_MARKER) || verbs[0].verb);
|
||||
|
||||
for (const Verb *verb = verbs; verb < verbs_end; verb++) {
|
||||
if (verb_is_metadata(verb))
|
||||
@@ -86,7 +96,7 @@ int _dispatch_verb(char **args, const Verb verbs[], const Verb verbs_end[], void
|
||||
assert(verbs);
|
||||
assert(verbs_end > verbs);
|
||||
assert((uintptr_t) verbs % sizeof(void*) == 0);
|
||||
assert(verbs[0].verb);
|
||||
assert(FLAGS_SET(verbs[0].flags, VERB_COMMAND_MARKER) || verbs[0].verb);
|
||||
|
||||
const char *name = args ? args[0] : NULL;
|
||||
size_t left = strv_length(args);
|
||||
@@ -214,6 +224,26 @@ static int verb_add_help_one(Table *table, const Verb *verb) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const Verb* verbs_get_command(const Verb verbs[], const Verb verbs_end[], const char *name) {
|
||||
assert(verbs);
|
||||
assert(verbs_end > verbs);
|
||||
assert((uintptr_t) verbs % sizeof(void*) == 0);
|
||||
assert(FLAGS_SET(verbs[0].flags, VERB_COMMAND_MARKER) || verbs[0].verb);
|
||||
assert(name);
|
||||
|
||||
for (const Verb *verb = verbs; verb < verbs_end; verb++) {
|
||||
if (!FLAGS_SET(verb->flags, VERB_COMMAND_MARKER))
|
||||
continue;
|
||||
|
||||
const CommandDescription *cmd = (const CommandDescription*) ASSERT_PTR(verb->data);
|
||||
if (nulstr_contains(cmd->names, name))
|
||||
return verb;
|
||||
}
|
||||
|
||||
/* At the end of the list? */
|
||||
return NULL;
|
||||
}
|
||||
|
||||
int _verbs_get_help_table(
|
||||
const Verb verbs[],
|
||||
const Verb verbs_end[],
|
||||
@@ -234,7 +264,18 @@ int _verbs_get_help_table(
|
||||
* group <group>? The first part is the default group, so
|
||||
* if the group was not specified, we are in. */
|
||||
|
||||
for (const Verb *verb = verbs; verb < verbs_end; verb++) {
|
||||
const Verb *verb;
|
||||
for (verb = verbs; verb < verbs_end; verb++) {
|
||||
/* Binaries with one or more VERB_COMMAND entries call this function
|
||||
* through print_verb_option_help() which provides a verbs slice that
|
||||
* starts after the VERB_COMMAND entry.
|
||||
*
|
||||
* In unconverted binaries, this function is called directly. */
|
||||
// TODO: make this function static after everything has been converted.
|
||||
|
||||
if (FLAGS_SET(verb->flags, VERB_COMMAND_MARKER))
|
||||
break;
|
||||
|
||||
assert(verb->verb);
|
||||
|
||||
bool group_marker = FLAGS_SET(verb->flags, VERB_GROUP_MARKER);
|
||||
@@ -256,5 +297,379 @@ int _verbs_get_help_table(
|
||||
|
||||
table_set_header(table, false);
|
||||
*ret = TAKE_PTR(table);
|
||||
|
||||
assert(verb - verbs < INT_MAX);
|
||||
return verb - verbs; /* Return the count of verb entries "consumed". */
|
||||
}
|
||||
|
||||
static void table_array_freep(Table ***tables) {
|
||||
assert(tables);
|
||||
|
||||
for (Table **t = *tables; t && *t; t++)
|
||||
*t = table_unref(*t);
|
||||
free(*tables);
|
||||
}
|
||||
|
||||
static int print_verb_option_help(
|
||||
const CommandDescription *cmd,
|
||||
const Verb verbs[],
|
||||
const Verb verbs_end[],
|
||||
const Option options[],
|
||||
const Option options_end[]) {
|
||||
|
||||
assert(verbs);
|
||||
assert(verbs_end >= verbs);
|
||||
assert((uintptr_t) verbs % sizeof(void*) == 0);
|
||||
|
||||
_cleanup_free_ const char **groups = NULL;
|
||||
_cleanup_(table_array_freep) Table **tables = NULL;
|
||||
size_t n_verbs = 0, n_opts = 0;
|
||||
int r;
|
||||
|
||||
for (const Verb *verb = verbs; verb < verbs_end;) {
|
||||
if (FLAGS_SET(verb->flags, VERB_COMMAND_MARKER))
|
||||
/* Start of entries for another command */
|
||||
break;
|
||||
|
||||
assert(verb->verb);
|
||||
|
||||
/* What is the the first group? */
|
||||
const char *group = FLAGS_SET(verb->flags, VERB_GROUP_MARKER) ? verb->verb : NULL;
|
||||
|
||||
_cleanup_(table_unrefp) Table *table = NULL;
|
||||
r = _verbs_get_help_table(verb, verbs_end, group, &table);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
if (!GREEDY_REALLOC0(tables, n_verbs + 2))
|
||||
return -ENOMEM;
|
||||
if (!GREEDY_REALLOC0(groups, n_verbs + 2))
|
||||
return -ENOMEM;
|
||||
|
||||
tables[n_verbs] = TAKE_PTR(table);
|
||||
groups[n_verbs++] = group ?: "Commands";
|
||||
|
||||
verb += r; /* Skip over the whole verb group */
|
||||
}
|
||||
|
||||
if (cmd->flags & COMMAND_HELP_SEPARATE)
|
||||
(void) table_sync_all_column_widths(0, tables);
|
||||
|
||||
const Option *optns = options_find_namespace(options, options_end, cmd->option_namespace);
|
||||
if (!optns)
|
||||
return log_error_errno(SYNTHETIC_ERRNO(EUCLEAN),
|
||||
"Option namespace %s not found.",
|
||||
cmd->option_namespace ?: "(unnamed)");
|
||||
|
||||
for (const Option *opt = optns; opt < options_end;) {
|
||||
_cleanup_(table_unrefp) Table *table = NULL;
|
||||
const char *group;
|
||||
|
||||
if (FLAGS_SET(opt->flags, OPTION_NAMESPACE_MARKER))
|
||||
/* End of our namespace */
|
||||
break;
|
||||
|
||||
r = options_get_help_table_group(opt, options_end, &table, &group);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
if (!GREEDY_REALLOC0(tables, n_verbs + n_opts + 2))
|
||||
return -ENOMEM;
|
||||
if (!GREEDY_REALLOC0(groups, n_verbs + n_opts + 2))
|
||||
return -ENOMEM;
|
||||
|
||||
tables[n_verbs + n_opts] = TAKE_PTR(table);
|
||||
groups[n_verbs + n_opts++] = group ?: "Options";
|
||||
|
||||
opt += r; /* Skip over the whole option group */
|
||||
}
|
||||
|
||||
if (cmd->flags & COMMAND_HELP_SEPARATE)
|
||||
(void) table_sync_all_column_widths(0, tables + n_verbs);
|
||||
else
|
||||
(void) table_sync_all_column_widths(0, tables);
|
||||
|
||||
for (size_t i = 0; i < n_verbs + n_opts; i++) {
|
||||
if (table_isempty(tables[i]))
|
||||
continue;
|
||||
|
||||
help_section(groups[i]);
|
||||
|
||||
r = table_print_or_warn(tables[i]);
|
||||
if (r < 0)
|
||||
return r;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int print_man_links(const char *pages) {
|
||||
int r;
|
||||
|
||||
NULSTR_FOREACH(page, pages) {
|
||||
_cleanup_free_ char *link = NULL;
|
||||
|
||||
r = terminal_urlify_man(page, /* section= */ NULL, &link);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
printf("\nSee the %s for details.\n", link);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static bool verbs_array_has_verbs(const Verb verbs[], const Verb verbs_end[]) {
|
||||
for (const Verb *verb = verbs; verb < verbs_end; verb++) {
|
||||
if (FLAGS_SET(verb->flags, VERB_COMMAND_MARKER)) /* start of another command section */
|
||||
break;
|
||||
if (!verb->help) /* a hidden verb */
|
||||
continue;
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
static int print_wrapped(const char *text, bool abstract) {
|
||||
|
||||
/* Print the text wrapped at spaces to the specified width. Newlines embedded in the text
|
||||
* are preserved and each line is wrapped independently, so explicit line breaks (e.g.
|
||||
* before an example command line) can be used where needed. */
|
||||
|
||||
if (!text)
|
||||
return 0;
|
||||
|
||||
_cleanup_strv_free_ char **lines = NULL, **lines2 = NULL;
|
||||
int r;
|
||||
|
||||
size_t width = MIN(columns(), 80U); /* This is a reasonable default. */
|
||||
|
||||
r = strv_split_full(&lines, text, "\n", EXTRACT_DONT_COALESCE_SEPARATORS);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
r = strv_rebreak_lines(lines, width, &lines2);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
/* Seperate this block from the previous input. */
|
||||
putchar('\n');
|
||||
|
||||
STRV_FOREACH(line, lines2)
|
||||
if (abstract)
|
||||
printf("%s%s%s%s\n",
|
||||
ansi_highlight(),
|
||||
ansi_add_italics(),
|
||||
*line,
|
||||
ansi_normal());
|
||||
else
|
||||
puts(*line);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int _command_print_help(
|
||||
const Verb verbs[],
|
||||
const Verb verbs_end[],
|
||||
const Option options[],
|
||||
const Option options_end[],
|
||||
const char *name) {
|
||||
int r;
|
||||
|
||||
const Verb *cmdverb = verbs_get_command(verbs, verbs_end, name);
|
||||
if (!cmdverb)
|
||||
return log_error_errno(SYNTHETIC_ERRNO(ENODATA),
|
||||
"Command %s not known", name);
|
||||
|
||||
const CommandDescription *cmd = (const CommandDescription*) ASSERT_PTR(cmdverb->data);
|
||||
|
||||
if (cmd->pager_flags)
|
||||
pager_open(*cmd->pager_flags);
|
||||
|
||||
if (cmd->argspec)
|
||||
NULSTR_FOREACH(spec, cmd->argspec) {
|
||||
_cleanup_free_ char *line = strjoin("[OPTION…] ", spec);
|
||||
if (!line)
|
||||
return log_oom();
|
||||
help_cmdline(line);
|
||||
}
|
||||
else {
|
||||
bool have_verbs = verbs_array_has_verbs(cmdverb + 1, verbs_end);
|
||||
help_cmdline(have_verbs ? "[OPTION…] COMMAND …" : "[OPTION…]");
|
||||
}
|
||||
|
||||
r = print_wrapped(cmd->abstract, /* abstract= */ true);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
r = print_verb_option_help(cmd, cmdverb + 1, verbs_end, options, options_end);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to print verb&option help: %m");
|
||||
|
||||
r = print_wrapped(cmd->footer, /* abstract= */ false);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
r = print_man_links(cmd->man_pages);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to print man page links: %m");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int verb_build_json(const Verb *verb, sd_json_variant **ret) {
|
||||
int r;
|
||||
|
||||
assert(verb);
|
||||
assert(verb->verb);
|
||||
assert(ret);
|
||||
|
||||
/* Verbs are represented as command objects, as described in the CLI-Introspection
|
||||
* Specification (https://uapi-group.org/specifications/specs/cli_introspection/).
|
||||
*
|
||||
* The "minArguments", "maxArguments", "isDefault", and "isOnlineOnly" fields
|
||||
* are extensions not (yet) covered by the specification. Note that the argument
|
||||
* counts include the verb itself. */
|
||||
|
||||
r = sd_json_buildo(
|
||||
ret,
|
||||
SD_JSON_BUILD_PAIR_STRV("names", STRV_MAKE(verb->verb)),
|
||||
SD_JSON_BUILD_PAIR_CONDITION(
|
||||
!!verb->help,
|
||||
"abstract", SD_JSON_BUILD_STRV(STRV_MAKE(verb->help))),
|
||||
SD_JSON_BUILD_PAIR_CONDITION(
|
||||
verb->min_args != VERB_ANY,
|
||||
"minArguments", SD_JSON_BUILD_UNSIGNED(verb->min_args)),
|
||||
SD_JSON_BUILD_PAIR_CONDITION(
|
||||
verb->max_args != VERB_ANY,
|
||||
"maxArguments", SD_JSON_BUILD_UNSIGNED(verb->max_args)),
|
||||
SD_JSON_BUILD_PAIR_CONDITION(
|
||||
FLAGS_SET(verb->flags, VERB_DEFAULT),
|
||||
"isDefault", SD_JSON_BUILD_BOOLEAN(true)),
|
||||
SD_JSON_BUILD_PAIR_CONDITION(
|
||||
FLAGS_SET(verb->flags, VERB_ONLINE_ONLY),
|
||||
"isOnlineOnly", SD_JSON_BUILD_BOOLEAN(true)));
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to build JSON object: %m");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int command_build_json(
|
||||
const Verb *cmdverb,
|
||||
const Verb verbs_end[],
|
||||
const Option options[],
|
||||
const Option options_end[],
|
||||
sd_json_variant **ret) {
|
||||
|
||||
_cleanup_(sd_json_variant_unrefp) sd_json_variant *names = NULL, *opts = NULL, *cmds = NULL;
|
||||
int r;
|
||||
|
||||
assert(cmdverb);
|
||||
assert(FLAGS_SET(cmdverb->flags, VERB_COMMAND_MARKER));
|
||||
assert(ret);
|
||||
|
||||
const CommandDescription *cmd = (const CommandDescription*) ASSERT_PTR(cmdverb->data);
|
||||
|
||||
NULSTR_FOREACH(name, cmd->names) {
|
||||
r = sd_json_variant_append_arrayb(&names, SD_JSON_BUILD_STRING(name));
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to append JSON string to array: %m");
|
||||
}
|
||||
assert(names); /* At least the primary name must be defined */
|
||||
|
||||
r = options_build_json(options, options_end, cmd->option_namespace, &opts);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
for (const Verb *verb = cmdverb + 1; verb < verbs_end; verb++) {
|
||||
if (FLAGS_SET(verb->flags, VERB_COMMAND_MARKER))
|
||||
break; /* Start of entries for another command */
|
||||
|
||||
if (FLAGS_SET(verb->flags, VERB_GROUP_MARKER))
|
||||
continue;
|
||||
|
||||
_cleanup_(sd_json_variant_unrefp) sd_json_variant *o = NULL;
|
||||
r = verb_build_json(verb, &o);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
r = sd_json_variant_append_array(&cmds, o);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to append JSON object to array: %m");
|
||||
}
|
||||
|
||||
_cleanup_strv_free_ char **features = strv_split(systemd_features, /* separators= */ NULL);
|
||||
if (!features)
|
||||
return log_oom();
|
||||
|
||||
r = sd_json_buildo(
|
||||
ret,
|
||||
SD_JSON_BUILD_PAIR_VARIANT("names", names),
|
||||
SD_JSON_BUILD_PAIR_STRING("version", PROJECT_VERSION_FULL),
|
||||
SD_JSON_BUILD_PAIR_STRV("features", features),
|
||||
SD_JSON_BUILD_PAIR_CONDITION(
|
||||
!!cmd->abstract,
|
||||
"abstract", SD_JSON_BUILD_STRV(STRV_MAKE(cmd->abstract))),
|
||||
SD_JSON_BUILD_PAIR_CONDITION(
|
||||
!!cmd->footer,
|
||||
"footer", SD_JSON_BUILD_STRV(STRV_MAKE(cmd->footer))),
|
||||
SD_JSON_BUILD_PAIR_CONDITION(!!opts, "options", SD_JSON_BUILD_VARIANT(opts)),
|
||||
SD_JSON_BUILD_PAIR_CONDITION(!!cmds, "verbs", SD_JSON_BUILD_VARIANT(cmds)));
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to build JSON object: %m");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int _introspect_cli(
|
||||
const Verb verbs[],
|
||||
const Verb verbs_end[],
|
||||
const Option options[],
|
||||
const Option options_end[],
|
||||
sd_json_format_flags_t flags) {
|
||||
|
||||
_cleanup_(sd_json_variant_unrefp) sd_json_variant *commands = NULL, *w = NULL;
|
||||
int r;
|
||||
|
||||
assert(verbs);
|
||||
assert(verbs_end > verbs);
|
||||
assert((uintptr_t) verbs % sizeof(void*) == 0);
|
||||
|
||||
if (flags == SD_JSON_FORMAT_OFF)
|
||||
flags = SD_JSON_FORMAT_PRETTY_AUTO;
|
||||
|
||||
for (const Verb *verb = verbs; verb < verbs_end; verb++) {
|
||||
if (!FLAGS_SET(verb->flags, VERB_COMMAND_MARKER))
|
||||
continue;
|
||||
|
||||
_cleanup_(sd_json_variant_unrefp) sd_json_variant *c = NULL;
|
||||
r = command_build_json(verb, verbs_end, options, options_end, &c);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
r = sd_json_variant_append_array(&commands, c);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to append JSON object to array: %m");
|
||||
}
|
||||
|
||||
if (!commands)
|
||||
return log_error_errno(SYNTHETIC_ERRNO(ENODATA),
|
||||
"No command description defined, cannot introspect.");
|
||||
|
||||
r = sd_json_buildo(
|
||||
&w,
|
||||
SD_JSON_BUILD_PAIR_STRING(
|
||||
"mediaType",
|
||||
"application/vnd.io.systemd.cli-introspection-0"),
|
||||
SD_JSON_BUILD_PAIR_VARIANT("commands", commands));
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to build JSON object: %m");
|
||||
|
||||
r = sd_json_variant_dump(w, flags, stdout, /* prefix= */ NULL);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to print JSON object: %m");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
+64
-4
@@ -2,17 +2,34 @@
|
||||
#pragma once
|
||||
|
||||
#include "forward.h"
|
||||
#include "options.h"
|
||||
|
||||
#define VERB_ANY (UINT_MAX)
|
||||
|
||||
typedef enum CommandFlags {
|
||||
COMMAND_HELP_SEPARATE = 1 << 0, /* Do not synchronize the width between verbs and options */
|
||||
} CommandFlags;
|
||||
|
||||
typedef struct CommandDescription {
|
||||
const char *names; /* nulstr with the main command name and potentially aliases */
|
||||
const char *abstract; /* concrete abstract */
|
||||
const char *argspec; /* optional specification of positional args in synopsis */
|
||||
const char *footer; /* optional footer to print right above man page links */
|
||||
const char *man_pages; /* nulstr with man page names */
|
||||
const char *option_namespace; /* to be used when the options are in a namespace */
|
||||
const PagerFlags *pager_flags; /* optional pointer to the runtime pager flags variable */
|
||||
CommandFlags flags;
|
||||
} CommandDescription;
|
||||
|
||||
typedef enum VerbFlags {
|
||||
VERB_DEFAULT = 1 << 0, /* The verb to run if no verb is specified */
|
||||
VERB_ONLINE_ONLY = 1 << 1, /* Just do nothing when running in chroot or offline */
|
||||
VERB_GROUP_MARKER = 1 << 2, /* Fake verb entry to separate groups */
|
||||
VERB_DEFAULT = 1 << 0, /* The verb to run if no verb is specified */
|
||||
VERB_ONLINE_ONLY = 1 << 1, /* Just do nothing when running in chroot or offline */
|
||||
VERB_COMMAND_MARKER = 1 << 2, /* Header entry with command description */
|
||||
VERB_GROUP_MARKER = 1 << 3, /* Fake verb entry to separate groups */
|
||||
} VerbFlags;
|
||||
|
||||
/* Note: see the comment on struct Option in options.h for why _alignptr_ is required here. */
|
||||
typedef struct _alignptr_ {
|
||||
typedef struct _alignptr_ Verb {
|
||||
const char *verb;
|
||||
unsigned min_args, max_args;
|
||||
VerbFlags flags;
|
||||
@@ -71,6 +88,12 @@ assert_cc(sizeof(Verb) % sizeof(void*) == 0);
|
||||
_VERB_DATA(/* d= */ NULL, /* v= */ gr, /* a= */ NULL, /* amin= */ 0, /* amax= */ 0, \
|
||||
/* f= */ VERB_GROUP_MARKER, /* dat= */ 0, /* h= */ NULL)
|
||||
|
||||
#define _COMMAND(u, ...) \
|
||||
static const CommandDescription UNIQ_T(description, u) = { __VA_ARGS__ }; \
|
||||
_VERB_DATA(/* d= */ NULL, /* v= */ NULL, /* a= */ NULL, /* amin= */ 0, /* amax= */ 0, \
|
||||
/* f= */ VERB_COMMAND_MARKER, /* dat= */ (uintptr_t) &UNIQ_T(description, u), /* h= */ NULL)
|
||||
#define COMMAND(...) _COMMAND(UNIQ, __VA_ARGS__)
|
||||
|
||||
/* This is magically mapped to the beginning and end of the section */
|
||||
extern const Verb __start_SYSTEMD_VERBS[];
|
||||
extern const Verb __stop_SYSTEMD_VERBS[];
|
||||
@@ -107,3 +130,40 @@ int _verbs_get_help_table(
|
||||
#define VERB_COMMON_HELP_HIDDEN(impl) \
|
||||
VERB(verb_help, "help", NULL, VERB_ANY, VERB_ANY, 0, NULL); \
|
||||
_VERB_COMMON_HELP_IMPL(impl)
|
||||
|
||||
#define VERB_COMMON_HELP_AUTO(program) \
|
||||
VERB_FULL(verb_help_auto, "help", NULL, VERB_ANY, VERB_ANY, 0, (uintptr_t) program, "Show this help")
|
||||
|
||||
#define VERB_COMMON_HELP_AUTO_HIDDEN(program) \
|
||||
VERB_FULL(verb_help_auto, "help", NULL, VERB_ANY, VERB_ANY, 0, (uintptr_t) program, NULL)
|
||||
|
||||
int _command_print_help(
|
||||
const Verb verbs[],
|
||||
const Verb verbs_end[],
|
||||
const Option options[],
|
||||
const Option options_end[],
|
||||
const char *name);
|
||||
#define command_print_help(name) \
|
||||
_command_print_help( \
|
||||
__start_SYSTEMD_VERBS, __stop_SYSTEMD_VERBS, \
|
||||
__start_SYSTEMD_OPTIONS, __stop_SYSTEMD_OPTIONS, \
|
||||
name)
|
||||
|
||||
static inline int verb_help_auto(int argc, char **argv, uintptr_t data, void *userdata) {
|
||||
return command_print_help((const char*) ASSERT_PTR(data));
|
||||
}
|
||||
|
||||
/* Print a machine-readable description of the program's commands, verbs, and options in the format
|
||||
* defined by the CLI-Introspection Specification. One command object is emitted for each
|
||||
* VERB_COMMAND() in the verb table, so multicall binaries are described in full. */
|
||||
int _introspect_cli(
|
||||
const Verb verbs[],
|
||||
const Verb verbs_end[],
|
||||
const Option options[],
|
||||
const Option options_end[],
|
||||
sd_json_format_flags_t flags);
|
||||
#define introspect_cli(flags) \
|
||||
_introspect_cli( \
|
||||
__start_SYSTEMD_VERBS, __stop_SYSTEMD_VERBS, \
|
||||
__start_SYSTEMD_OPTIONS, __stop_SYSTEMD_OPTIONS, \
|
||||
flags)
|
||||
|
||||
+12
-50
@@ -24,7 +24,6 @@
|
||||
#include "parse-argument.h"
|
||||
#include "parse-util.h"
|
||||
#include "polkit-agent.h"
|
||||
#include "pretty-print.h"
|
||||
#include "runtime-scope.h"
|
||||
#include "sparse-endian.h"
|
||||
#include "string-table.h"
|
||||
@@ -180,6 +179,13 @@ static int print_status_info(const StatusInfo *i) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
COMMAND(
|
||||
"timedatectl\0",
|
||||
.abstract = "Query or change system time and date settings.",
|
||||
.man_pages = "timedatectl.1\0",
|
||||
.pager_flags = &arg_pager_flags,
|
||||
);
|
||||
|
||||
VERB_DEFAULT_NOARG(verb_status, "status", "Show current time settings");
|
||||
static int verb_status(int argc, char *argv[], uintptr_t _data, void *userdata) {
|
||||
StatusInfo info = {};
|
||||
@@ -893,54 +899,7 @@ static int verb_revert(int argc, char *argv[], uintptr_t _data, void *userdata)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int help(void) {
|
||||
_cleanup_free_ char *link = NULL;
|
||||
_cleanup_(table_unrefp) Table *verbs = NULL, *verbs2 = NULL, *options = NULL;
|
||||
int r;
|
||||
|
||||
r = terminal_urlify_man("timedatectl", "1", &link);
|
||||
if (r < 0)
|
||||
return log_oom();
|
||||
|
||||
r = verbs_get_help_table(&verbs);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
r = verbs_get_help_table_group("systemd-timesyncd Commands", &verbs2);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
r = option_parser_get_help_table(&options);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
(void) table_sync_column_widths(0, verbs, verbs2, options);
|
||||
|
||||
printf("%s [OPTIONS...] COMMAND ...\n"
|
||||
"\n%sQuery or change system time and date settings.%s\n"
|
||||
"\nCommands:\n",
|
||||
program_invocation_short_name,
|
||||
ansi_highlight(),
|
||||
ansi_normal());
|
||||
r = table_print_or_warn(verbs);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
printf("\nsystemd-timesyncd Commands:\n");
|
||||
r = table_print_or_warn(verbs2);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
printf("\nOptions:\n");
|
||||
r = table_print_or_warn(options);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
printf("\nSee the %s for details.\n", link);
|
||||
return 0;
|
||||
}
|
||||
|
||||
VERB_COMMON_HELP_HIDDEN(help);
|
||||
VERB_COMMON_HELP_AUTO_HIDDEN("timedatectl");
|
||||
|
||||
static int parse_argv(int argc, char *argv[], char ***ret_args) {
|
||||
int r;
|
||||
@@ -953,7 +912,7 @@ static int parse_argv(int argc, char *argv[], char ***ret_args) {
|
||||
FOREACH_OPTION_OR_RETURN(c, &opts)
|
||||
switch (c) {
|
||||
OPTION_COMMON_HELP:
|
||||
return help();
|
||||
return command_print_help("timedatectl");
|
||||
|
||||
OPTION_COMMON_VERSION:
|
||||
return version();
|
||||
@@ -1005,6 +964,9 @@ static int parse_argv(int argc, char *argv[], char ***ret_args) {
|
||||
OPTION('a', "all", NULL, "Show all properties, including empty ones"):
|
||||
SET_FLAG(arg_print_flags, BUS_PRINT_PROPERTY_SHOW_EMPTY, true);
|
||||
break;
|
||||
|
||||
OPTION_COMMON_INTROSPECT_CLI:
|
||||
return introspect_cli(SD_JSON_FORMAT_OFF);
|
||||
}
|
||||
|
||||
*ret_args = option_parser_get_args(&opts);
|
||||
|
||||
+15
-43
@@ -23,7 +23,6 @@
|
||||
#include "format-table.h"
|
||||
#include "format-util.h"
|
||||
#include "fs-util.h"
|
||||
#include "help-util.h"
|
||||
#include "log.h"
|
||||
#include "main-func.h"
|
||||
#include "memfd-util.h"
|
||||
@@ -81,47 +80,6 @@ static void push_fds_done(PushFds *p) {
|
||||
STATIC_DESTRUCTOR_REGISTER(arg_graceful, strv_freep);
|
||||
STATIC_DESTRUCTOR_REGISTER(arg_push_fds, push_fds_done);
|
||||
|
||||
static int help(void) {
|
||||
_cleanup_(table_unrefp) Table *options = NULL, *verbs = NULL;
|
||||
int r;
|
||||
|
||||
r = option_parser_get_help_table(&options);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
r = verbs_get_help_table(&verbs);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
(void) table_sync_column_widths(0, options, verbs);
|
||||
|
||||
pager_open(arg_pager_flags);
|
||||
|
||||
help_cmdline("[OPTIONS...] COMMAND ...");
|
||||
help_abstract("Introspect Varlink Services.");
|
||||
|
||||
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;
|
||||
|
||||
printf("\nWith --exec, specify the command to invoke:\n"
|
||||
" %s --exec call ADDRESS METHOD PARAMS -- CMDLINE…\n",
|
||||
program_invocation_short_name);
|
||||
|
||||
help_man_page_reference("varlinkctl", "1");
|
||||
return 0;
|
||||
}
|
||||
|
||||
VERB_COMMON_HELP(help);
|
||||
|
||||
static int parse_argv(int argc, char *argv[], char ***ret_args) {
|
||||
int r;
|
||||
|
||||
@@ -134,7 +92,7 @@ static int parse_argv(int argc, char *argv[], char ***ret_args) {
|
||||
switch (c) {
|
||||
|
||||
OPTION_COMMON_HELP:
|
||||
return help();
|
||||
return command_print_help("varlinkctl");
|
||||
|
||||
OPTION_COMMON_VERSION:
|
||||
return version();
|
||||
@@ -248,6 +206,9 @@ static int parse_argv(int argc, char *argv[], char ***ret_args) {
|
||||
arg_push_fds.fds[arg_push_fds.n_fds++] = TAKE_FD(add_fd);
|
||||
break;
|
||||
}
|
||||
|
||||
OPTION_COMMON_INTROSPECT_CLI:
|
||||
return introspect_cli(arg_json_format_flags);
|
||||
}
|
||||
|
||||
/* If more than one reply is expected, imply JSON-SEQ output, and set SD_JSON_FORMAT_FLUSH */
|
||||
@@ -325,6 +286,15 @@ static void get_info_data_done(GetInfoData *d) {
|
||||
d->interfaces = strv_free(d->interfaces);
|
||||
}
|
||||
|
||||
COMMAND(
|
||||
"varlinkctl\0",
|
||||
"Introspect Varlink Services.",
|
||||
.man_pages = "varlinkctl.1\0",
|
||||
.footer = "With --exec, specify the command to invoke:\n"
|
||||
" varlinkctl --exec call ADDRESS METHOD PARAMS -- CMDLINE…",
|
||||
.pager_flags = &arg_pager_flags,
|
||||
);
|
||||
|
||||
VERB(verb_info, "info", "ADDRESS", 2, 2, 0, "Show service information");
|
||||
VERB(verb_info, "list-interfaces", "ADDRESS", 2, 2, 0, "List interfaces implemented by service");
|
||||
static int verb_info(int argc, char *argv[], uintptr_t _data, void *userdata) {
|
||||
@@ -1488,6 +1458,8 @@ static int verb_serve(int argc, char *argv[], uintptr_t _data, void *userdata) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
VERB_COMMON_HELP_AUTO("varlinkctl");
|
||||
|
||||
static int run(int argc, char *argv[]) {
|
||||
int r;
|
||||
|
||||
|
||||
@@ -4,16 +4,15 @@
|
||||
#include <sys/stat.h>
|
||||
|
||||
#include "sd-device.h"
|
||||
#include "sd-json.h"
|
||||
|
||||
#include "alloc-util.h"
|
||||
#include "argv-util.h"
|
||||
#include "build.h"
|
||||
#include "cryptsetup-util.h"
|
||||
#include "dlopen-note.h"
|
||||
#include "extract-word.h"
|
||||
#include "fileio.h"
|
||||
#include "format-table.h"
|
||||
#include "fstab-util.h"
|
||||
#include "help-util.h"
|
||||
#include "hexdecoct.h"
|
||||
#include "log.h"
|
||||
#include "main-func.h"
|
||||
@@ -51,26 +50,6 @@ STATIC_DESTRUCTOR_REGISTER(arg_fec_what, freep);
|
||||
STATIC_DESTRUCTOR_REGISTER(arg_root_hash_signature, freep);
|
||||
STATIC_DESTRUCTOR_REGISTER(arg_tpm2_measure_nvpcr, freep);
|
||||
|
||||
static int help(void) {
|
||||
_cleanup_(table_unrefp) Table *verbs = NULL;
|
||||
int r;
|
||||
|
||||
r = verbs_get_help_table(&verbs);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
help_cmdline("COMMAND ...");
|
||||
help_abstract("Attach or detach a verity protected block device.");
|
||||
|
||||
help_section("Commands");
|
||||
r = table_print_or_warn(verbs);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
help_man_page_reference("systemd-veritysetup@.service", "8");
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int parse_roothashsig_option(const char *option, bool strict) {
|
||||
_cleanup_free_ void *rhs = NULL;
|
||||
size_t rhss = 0;
|
||||
@@ -318,8 +297,14 @@ static int parse_options(const char *options) {
|
||||
return r;
|
||||
}
|
||||
|
||||
COMMAND(
|
||||
"systemd-veritysetup\0",
|
||||
"Attach or detach a verity-protected block device.",
|
||||
.man_pages = "systemd-veritysetup@.service.8\0",
|
||||
);
|
||||
|
||||
VERB(verb_attach, "attach", "VOLUME DATADEVICE HASHDEVICE ROOTHASH [OPTIONS]", 5, 6, 0,
|
||||
"Attach a verity protected block device");
|
||||
"Attach a verity-protected block device");
|
||||
static int verb_attach(int argc, char *argv[], uintptr_t _data, void *userdata) {
|
||||
_cleanup_(crypt_freep) struct crypt_device *cd = NULL;
|
||||
_cleanup_free_ void *rh = NULL;
|
||||
@@ -457,7 +442,7 @@ static int verb_attach(int argc, char *argv[], uintptr_t _data, void *userdata)
|
||||
}
|
||||
|
||||
VERB(verb_detach, "detach", "VOLUME", 2, 2, 0,
|
||||
"Detach a verity protected block device");
|
||||
"Detach a verity-protected block device");
|
||||
static int verb_detach(int argc, char *argv[], uintptr_t _data, void *userdata) {
|
||||
_cleanup_(crypt_freep) struct crypt_device *cd = NULL;
|
||||
int r;
|
||||
@@ -486,11 +471,39 @@ static int verb_detach(int argc, char *argv[], uintptr_t _data, void *userdata)
|
||||
return 0;
|
||||
}
|
||||
|
||||
VERB_COMMON_HELP_AUTO_HIDDEN("systemd-veritysetup");
|
||||
|
||||
static int parse_argv(int argc, char *argv[], char ***ret_args) {
|
||||
assert(argc >= 0);
|
||||
assert(argv);
|
||||
assert(ret_args);
|
||||
|
||||
OptionParser opts = { argc, argv };
|
||||
|
||||
FOREACH_OPTION_OR_RETURN(c, &opts)
|
||||
switch (c) {
|
||||
|
||||
OPTION_COMMON_HELP:
|
||||
return command_print_help("systemd-veritysetup");
|
||||
|
||||
OPTION_COMMON_VERSION:
|
||||
return version();
|
||||
|
||||
OPTION_COMMON_INTROSPECT_CLI:
|
||||
return introspect_cli(SD_JSON_FORMAT_OFF);
|
||||
}
|
||||
|
||||
*ret_args = option_parser_get_args(&opts);
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int run(int argc, char *argv[]) {
|
||||
int r;
|
||||
|
||||
if (argv_looks_like_help(argc, argv))
|
||||
return help();
|
||||
char **args = NULL;
|
||||
r = parse_argv(argc, argv, &args);
|
||||
if (r <= 0)
|
||||
return r;
|
||||
|
||||
log_setup();
|
||||
|
||||
@@ -503,7 +516,7 @@ static int run(int argc, char *argv[]) {
|
||||
|
||||
umask(0022);
|
||||
|
||||
return dispatch_verb(strv_skip(argv, 1), /* userdata= */ NULL);
|
||||
return dispatch_verb(args, /* userdata= */ NULL);
|
||||
}
|
||||
|
||||
DEFINE_MAIN_FUNCTION(run);
|
||||
|
||||
+35
@@ -0,0 +1,35 @@
|
||||
#!/usr/bin/env bash
|
||||
# SPDX-License-Identifier: LGPL-2.1-or-later
|
||||
set -eux
|
||||
set -o pipefail
|
||||
|
||||
# Just a smoke test for the introspection code
|
||||
for i in systemd-ac-power systemd-analyze systemd-ask-password systemd-dissect \
|
||||
systemd-id128 systemd-notify homectl resolvectl timedatectl varlinkctl; do
|
||||
command -v "$i" >/dev/null || continue
|
||||
|
||||
$i --introspect-cli | jq -e \
|
||||
'.mediaType == "application/vnd.io.systemd.cli-introspection-0"'
|
||||
$i --introspect-cli | jq -e --arg name "$i" \
|
||||
'any(.commands[]; .names[0] == $name)'
|
||||
$i --introspect-cli | jq -e \
|
||||
'any(.commands[]; [.options[].names[-1]] | contains(["--help", "--version", "--introspect-cli"]))'
|
||||
$i --intro | grep -e --help
|
||||
done
|
||||
|
||||
# Those are not in $PATH and do not do regular option parsing, so the option must
|
||||
# be given verbatim
|
||||
for i in /usr/lib/systemd/systemd-backlight /usr/lib/systemd/systemd-veritysetup; do
|
||||
[[ -x "$i" ]] || continue
|
||||
|
||||
$i --introspect-cli | jq -e \
|
||||
'.mediaType == "application/vnd.io.systemd.cli-introspection-0"'
|
||||
done
|
||||
|
||||
# systemd-id128 defines verbs, check that they are described
|
||||
systemd-id128 --introspect-cli | jq -e \
|
||||
'.commands[0].verbs | map(.names[0]) | contains(["new", "machine-id", "show", "help"])'
|
||||
|
||||
# resolvectl is a multicall binary, check that both commands are described
|
||||
resolvectl --introspect-cli | jq -e \
|
||||
'[.commands[].names[0]] | sort == ["resolvconf", "resolvectl", "systemd-resolve"]'
|
||||
Reference in New Issue
Block a user