Refactor the option & verb table handling and convert a few more programs (#41335)
After having some more experience with how this works, I think some changes are in order. So there are a handful of preparatory patches and then conversion of a few progs that make use of the new functionality.
This commit is contained in:
@@ -48,10 +48,10 @@ static int parse_argv(int argc, char *argv[]) {
|
||||
assert(argc >= 0);
|
||||
assert(argv);
|
||||
|
||||
OptionParser state = {};
|
||||
OptionParser state = { argc, argv };
|
||||
const char *arg;
|
||||
|
||||
FOREACH_OPTION(&state, c, argc, argv, &arg, /* on_error= */ return c)
|
||||
FOREACH_OPTION(&state, c, &arg, /* on_error= */ return c)
|
||||
switch (c) {
|
||||
OPTION_COMMON_HELP:
|
||||
return help();
|
||||
@@ -68,7 +68,7 @@ static int parse_argv(int argc, char *argv[]) {
|
||||
break;
|
||||
}
|
||||
|
||||
if (option_parser_get_n_args(&state, argc, argv) > 0)
|
||||
if (option_parser_get_n_args(&state) > 0)
|
||||
return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "This program takes no arguments.");
|
||||
|
||||
return 1;
|
||||
|
||||
@@ -71,10 +71,10 @@ static int parse_argv(int argc, char *argv[]) {
|
||||
assert(argc >= 0);
|
||||
assert(argv);
|
||||
|
||||
OptionParser state = {};
|
||||
OptionParser state = { argc, argv };
|
||||
const char *arg;
|
||||
|
||||
FOREACH_OPTION(&state, c, argc, argv, &arg, /* on_error= */ return c)
|
||||
FOREACH_OPTION(&state, c, &arg, /* on_error= */ return c)
|
||||
switch (c) {
|
||||
OPTION_COMMON_HELP:
|
||||
return help();
|
||||
@@ -170,7 +170,7 @@ static int parse_argv(int argc, char *argv[]) {
|
||||
SET_FLAG(arg_flags, ASK_PASSWORD_HIDE_EMOJI, !r);
|
||||
}
|
||||
|
||||
char **args = option_parser_get_args(&state, argc, argv);
|
||||
char **args = option_parser_get_args(&state);
|
||||
|
||||
if (!strv_isempty(args)) {
|
||||
arg_message = strv_join(args, " ");
|
||||
|
||||
+3
-3
@@ -137,10 +137,10 @@ static int parse_argv(int argc, char *argv[], char ***ret_args) {
|
||||
assert(argc >= 0);
|
||||
assert(argv);
|
||||
|
||||
OptionParser state = {};
|
||||
OptionParser state = { argc, argv };
|
||||
const char *arg;
|
||||
|
||||
FOREACH_OPTION(&state, c, argc, argv, &arg, /* on_error= */ return c)
|
||||
FOREACH_OPTION(&state, c, &arg, /* on_error= */ return c)
|
||||
switch (c) {
|
||||
OPTION_COMMON_HELP:
|
||||
return help();
|
||||
@@ -165,7 +165,7 @@ static int parse_argv(int argc, char *argv[], char ***ret_args) {
|
||||
break;
|
||||
}
|
||||
|
||||
char **args = option_parser_get_args(&state, argc, argv);
|
||||
char **args = option_parser_get_args(&state);
|
||||
|
||||
if ((arg_unregister || arg_cat_flags != CAT_CONFIG_OFF) && !strv_isempty(args))
|
||||
return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
|
||||
|
||||
@@ -50,7 +50,7 @@ static int help(void) {
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
(void) table_sync_column_width(options, 0, verbs, 0);
|
||||
(void) table_sync_column_widths(0, options, verbs);
|
||||
|
||||
printf("%s [OPTIONS...] COMMAND\n"
|
||||
"\n%sMark the boot process as good or bad.%s\n"
|
||||
@@ -75,10 +75,10 @@ static int parse_argv(int argc, char *argv[], char ***ret_args) {
|
||||
assert(argc >= 0);
|
||||
assert(argv);
|
||||
|
||||
OptionParser state = {};
|
||||
OptionParser state = { argc, argv };
|
||||
const char *arg;
|
||||
|
||||
FOREACH_OPTION(&state, c, argc, argv, &arg, /* on_error= */ return c)
|
||||
FOREACH_OPTION(&state, c, &arg, /* on_error= */ return c)
|
||||
switch (c) {
|
||||
OPTION_COMMON_HELP:
|
||||
return help();
|
||||
@@ -93,7 +93,7 @@ static int parse_argv(int argc, char *argv[], char ***ret_args) {
|
||||
break;
|
||||
}
|
||||
|
||||
*ret_args = option_parser_get_args(&state, argc, argv);
|
||||
*ret_args = option_parser_get_args(&state);
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,12 +1,13 @@
|
||||
/* SPDX-License-Identifier: LGPL-2.1-or-later */
|
||||
|
||||
#include <getopt.h>
|
||||
|
||||
#include "alloc-util.h"
|
||||
#include "ansi-color.h"
|
||||
#include "build.h"
|
||||
#include "confidential-virt.h"
|
||||
#include "format-table.h"
|
||||
#include "log.h"
|
||||
#include "main-func.h"
|
||||
#include "options.h"
|
||||
#include "pretty-print.h"
|
||||
#include "string-table.h"
|
||||
#include "virt.h"
|
||||
@@ -23,109 +24,76 @@ static enum {
|
||||
|
||||
static int help(void) {
|
||||
_cleanup_free_ char *link = NULL;
|
||||
_cleanup_(table_unrefp) Table *options = NULL;
|
||||
int r;
|
||||
|
||||
r = terminal_urlify_man("systemd-detect-virt", "1", &link);
|
||||
if (r < 0)
|
||||
return log_oom();
|
||||
|
||||
printf("%s [OPTIONS...]\n\n"
|
||||
"Detect execution in a virtualized environment.\n\n"
|
||||
" -h --help Show this help\n"
|
||||
" --version Show package version\n"
|
||||
" -c --container Only detect whether we are run in a container\n"
|
||||
" -v --vm Only detect whether we are run in a VM\n"
|
||||
" -r --chroot Detect whether we are run in a chroot() environment\n"
|
||||
" --private-users Only detect whether we are running in a user namespace\n"
|
||||
" --cvm Only detect whether we are run in a confidential VM\n"
|
||||
" -q --quiet Don't output anything, just set return value\n"
|
||||
" --list List all known and detectable types of virtualization\n"
|
||||
" --list-cvm List all known and detectable types of confidential \n"
|
||||
" virtualization\n"
|
||||
"\nSee the %s for details.\n",
|
||||
program_invocation_short_name,
|
||||
link);
|
||||
r = option_parser_get_help_table(&options);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
printf("%s [OPTIONS...]\n"
|
||||
"\n%sDetect execution in a virtualized environment.%s\n"
|
||||
"\nOptions:\n",
|
||||
program_invocation_short_name,
|
||||
ansi_highlight(),
|
||||
ansi_normal());
|
||||
table_print(options, stdout);
|
||||
|
||||
printf("\nSee the %s for details.\n", link);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int parse_argv(int argc, char *argv[]) {
|
||||
|
||||
enum {
|
||||
ARG_VERSION = 0x100,
|
||||
ARG_PRIVATE_USERS,
|
||||
ARG_LIST,
|
||||
ARG_CVM,
|
||||
ARG_LIST_CVM,
|
||||
};
|
||||
|
||||
static const struct option options[] = {
|
||||
{ "help", no_argument, NULL, 'h' },
|
||||
{ "version", no_argument, NULL, ARG_VERSION },
|
||||
{ "container", no_argument, NULL, 'c' },
|
||||
{ "vm", no_argument, NULL, 'v' },
|
||||
{ "chroot", no_argument, NULL, 'r' },
|
||||
{ "private-users", no_argument, NULL, ARG_PRIVATE_USERS },
|
||||
{ "quiet", no_argument, NULL, 'q' },
|
||||
{ "cvm", no_argument, NULL, ARG_CVM },
|
||||
{ "list", no_argument, NULL, ARG_LIST },
|
||||
{ "list-cvm", no_argument, NULL, ARG_LIST_CVM },
|
||||
{}
|
||||
};
|
||||
|
||||
int c;
|
||||
|
||||
assert(argc >= 0);
|
||||
assert(argv);
|
||||
|
||||
while ((c = getopt_long(argc, argv, "hqcvr", options, NULL)) >= 0)
|
||||
OptionParser state = { argc, argv };
|
||||
const char *arg;
|
||||
|
||||
FOREACH_OPTION(&state, c, &arg, /* on_error= */ return c)
|
||||
switch (c) {
|
||||
|
||||
case 'h':
|
||||
OPTION_COMMON_HELP:
|
||||
return help();
|
||||
|
||||
case ARG_VERSION:
|
||||
OPTION_COMMON_VERSION:
|
||||
return version();
|
||||
|
||||
case 'q':
|
||||
OPTION('q', "quiet", NULL, "Don't output anything, just set return value"):
|
||||
arg_quiet = true;
|
||||
break;
|
||||
|
||||
case 'c':
|
||||
OPTION('c', "container", NULL, "Only detect whether we are run in a container"):
|
||||
arg_mode = ONLY_CONTAINER;
|
||||
break;
|
||||
|
||||
case ARG_PRIVATE_USERS:
|
||||
OPTION_LONG("private-users", NULL, "Only detect whether we are running in a user namespace"):
|
||||
arg_mode = ONLY_PRIVATE_USERS;
|
||||
break;
|
||||
|
||||
case 'v':
|
||||
OPTION('v', "vm", NULL, "Only detect whether we are run in a VM"):
|
||||
arg_mode = ONLY_VM;
|
||||
break;
|
||||
|
||||
case 'r':
|
||||
OPTION('r', "chroot", NULL, "Detect whether we are run in a chroot() environment"):
|
||||
arg_mode = ONLY_CHROOT;
|
||||
break;
|
||||
|
||||
case ARG_LIST:
|
||||
OPTION_LONG("list", NULL, "List all known and detectable types of virtualization"):
|
||||
return DUMP_STRING_TABLE(virtualization, Virtualization, _VIRTUALIZATION_MAX);
|
||||
|
||||
case ARG_CVM:
|
||||
OPTION_LONG("cvm", NULL, "Only detect whether we are run in a confidential VM"):
|
||||
arg_mode = ONLY_CVM;
|
||||
return 1;
|
||||
|
||||
case ARG_LIST_CVM:
|
||||
OPTION_LONG("list-cvm", NULL, "List all known and detectable types of confidential virtualization"):
|
||||
return DUMP_STRING_TABLE(confidential_virtualization, ConfidentialVirtualization, _CONFIDENTIAL_VIRTUALIZATION_MAX);
|
||||
|
||||
case '?':
|
||||
return -EINVAL;
|
||||
|
||||
default:
|
||||
assert_not_reached();
|
||||
}
|
||||
|
||||
if (optind < argc)
|
||||
if (option_parser_get_n_args(&state) > 0)
|
||||
return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
|
||||
"%s takes no arguments.",
|
||||
program_invocation_short_name);
|
||||
|
||||
@@ -141,7 +141,7 @@ static int help(void) {
|
||||
return r;
|
||||
|
||||
/* Make the 1st column same width in both tables */
|
||||
(void) table_sync_column_width(options, 0, commands, 0);
|
||||
(void) table_sync_column_widths(0, options, commands);
|
||||
|
||||
printf("%1$s [OPTIONS...] IMAGE\n"
|
||||
"%1$s [OPTIONS...] --mount IMAGE PATH\n"
|
||||
@@ -207,11 +207,11 @@ static int parse_argv(int argc, char *argv[]) {
|
||||
assert(argc >= 0);
|
||||
assert(argv);
|
||||
|
||||
OptionParser state = {};
|
||||
OptionParser state = { argc, argv };
|
||||
const Option *current;
|
||||
const char *arg;
|
||||
|
||||
FOREACH_OPTION_FULL(&state, c, argc, argv, ¤t, &arg, /* on_error= */ return c)
|
||||
FOREACH_OPTION_FULL(&state, c, ¤t, &arg, /* on_error= */ return c)
|
||||
switch (c) {
|
||||
|
||||
OPTION_COMMON_NO_PAGER:
|
||||
@@ -495,7 +495,7 @@ static int parse_argv(int argc, char *argv[]) {
|
||||
arg_runtime_scope = system_scope_requested && user_scope_requested ? _RUNTIME_SCOPE_INVALID :
|
||||
system_scope_requested ? RUNTIME_SCOPE_SYSTEM : RUNTIME_SCOPE_USER;
|
||||
|
||||
char **args = option_parser_get_args(&state, argc, argv);
|
||||
char **args = option_parser_get_args(&state);
|
||||
|
||||
switch (arg_action) {
|
||||
|
||||
|
||||
@@ -1,7 +1,5 @@
|
||||
/* SPDX-License-Identifier: LGPL-2.1-or-later */
|
||||
|
||||
#include <getopt.h>
|
||||
|
||||
#include "sd-json.h"
|
||||
#include "sd-varlink.h"
|
||||
|
||||
@@ -12,9 +10,11 @@
|
||||
#include "efivars.h"
|
||||
#include "errno-util.h"
|
||||
#include "factory-reset.h"
|
||||
#include "format-table.h"
|
||||
#include "fs-util.h"
|
||||
#include "json-util.h"
|
||||
#include "main-func.h"
|
||||
#include "options.h"
|
||||
#include "os-util.h"
|
||||
#include "pretty-print.h"
|
||||
#include "udev-util.h"
|
||||
@@ -28,76 +28,62 @@ static bool arg_varlink = false;
|
||||
|
||||
static int help(void) {
|
||||
_cleanup_free_ char *link = NULL;
|
||||
_cleanup_(table_unrefp) Table *options = NULL, *verbs = NULL;
|
||||
int r;
|
||||
|
||||
r = terminal_urlify_man("systemd-factory-reset", "8", &link);
|
||||
if (r < 0)
|
||||
return log_oom();
|
||||
|
||||
printf("%1$s [OPTIONS...] COMMAND\n"
|
||||
"\n%5$sQuery, request, cancel factory reset operation.%6$s\n"
|
||||
"\n%3$sCommands:%4$s\n"
|
||||
" status Report current factory reset status\n"
|
||||
" request Request a factory reset on next boot\n"
|
||||
" cancel Cancel a prior factory reset request for next boot\n"
|
||||
" complete Mark a factory reset as complete\n"
|
||||
"\n%3$sOptions:%4$s\n"
|
||||
" -h --help Show this help\n"
|
||||
" --version Print version\n"
|
||||
" --retrigger Retrigger block devices\n"
|
||||
" -q --quiet Suppress output\n"
|
||||
"\nSee the %2$s for details.\n",
|
||||
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);
|
||||
|
||||
printf("%s [OPTIONS...] COMMAND\n"
|
||||
"\n%sQuery, request, cancel factory reset operation.%s\n"
|
||||
"\nCommands:\n",
|
||||
program_invocation_short_name,
|
||||
link,
|
||||
ansi_underline(),
|
||||
ansi_normal(),
|
||||
ansi_highlight(),
|
||||
ansi_normal());
|
||||
table_print(verbs, stdout);
|
||||
|
||||
printf("\nOptions:\n");
|
||||
table_print(options, stdout);
|
||||
|
||||
printf("\nSee the %s for details.\n", link);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int parse_argv(int argc, char *argv[]) {
|
||||
enum {
|
||||
ARG_VERSION = 0x100,
|
||||
ARG_RETRIGGER,
|
||||
};
|
||||
|
||||
static const struct option options[] = {
|
||||
{ "help", no_argument, NULL, 'h' },
|
||||
{ "version", no_argument, NULL, ARG_VERSION },
|
||||
{ "retrigger", no_argument, NULL, ARG_RETRIGGER },
|
||||
{ "quiet", no_argument, NULL, 'q' },
|
||||
{}
|
||||
};
|
||||
|
||||
int r, c;
|
||||
static int parse_argv(int argc, char *argv[], char ***ret_args) {
|
||||
int r;
|
||||
|
||||
assert(argc >= 0);
|
||||
assert(argv);
|
||||
|
||||
while ((c = getopt_long(argc, argv, "hq", options, NULL)) >= 0)
|
||||
switch (c) {
|
||||
OptionParser state = { argc, argv };
|
||||
const char *arg;
|
||||
|
||||
case 'h':
|
||||
FOREACH_OPTION(&state, c, &arg, /* on_error= */ return c)
|
||||
switch (c) {
|
||||
OPTION_COMMON_HELP:
|
||||
return help();
|
||||
|
||||
case ARG_VERSION:
|
||||
OPTION_COMMON_VERSION:
|
||||
return version();
|
||||
|
||||
case ARG_RETRIGGER:
|
||||
OPTION_LONG("retrigger", NULL, "Retrigger block devices"):
|
||||
arg_retrigger = true;
|
||||
break;
|
||||
|
||||
case 'q':
|
||||
OPTION('q', "quiet", NULL, "Suppress output"):
|
||||
arg_quiet = true;
|
||||
break;
|
||||
|
||||
case '?':
|
||||
return -EINVAL;
|
||||
|
||||
default:
|
||||
assert_not_reached();
|
||||
}
|
||||
|
||||
r = sd_varlink_invocation(SD_VARLINK_ALLOW_ACCEPT);
|
||||
@@ -106,9 +92,11 @@ static int parse_argv(int argc, char *argv[]) {
|
||||
if (r > 0)
|
||||
arg_varlink = true;
|
||||
|
||||
*ret_args = option_parser_get_args(&state);
|
||||
return 1;
|
||||
}
|
||||
|
||||
VERB(verb_status, "status", NULL, VERB_ANY, 1, VERB_DEFAULT, "Report current factory reset status");
|
||||
static int verb_status(int argc, char *argv[], uintptr_t _data, void *userdata) {
|
||||
static const int exit_status_table[_FACTORY_RESET_MODE_MAX] = {
|
||||
/* Report current mode also as via exit status, but only return a subset of states */
|
||||
@@ -130,6 +118,7 @@ static int verb_status(int argc, char *argv[], uintptr_t _data, void *userdata)
|
||||
return exit_status_table[f];
|
||||
}
|
||||
|
||||
VERB_NOARG(verb_request, "request", "Request a factory reset on next boot");
|
||||
static int verb_request(int argc, char *argv[], uintptr_t _data, void *userdata) {
|
||||
int r;
|
||||
|
||||
@@ -197,6 +186,7 @@ static int verb_request(int argc, char *argv[], uintptr_t _data, void *userdata)
|
||||
return 0;
|
||||
}
|
||||
|
||||
VERB_NOARG(verb_cancel, "cancel", "Cancel a prior factory reset request for next boot");
|
||||
static int verb_cancel(int argc, char *argv[], uintptr_t _data, void *userdata) {
|
||||
int r;
|
||||
|
||||
@@ -269,6 +259,7 @@ static int retrigger_block_devices(void) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
VERB_NOARG(verb_complete, "complete", "Mark a factory reset as complete");
|
||||
static int verb_complete(int argc, char *argv[], uintptr_t _data, void *userdata) {
|
||||
int r;
|
||||
|
||||
@@ -359,26 +350,19 @@ static int varlink_service(void) {
|
||||
}
|
||||
|
||||
static int run(int argc, char *argv[]) {
|
||||
static const Verb verbs[] = {
|
||||
{ "status", VERB_ANY, 1, VERB_DEFAULT, verb_status },
|
||||
{ "request", VERB_ANY, 1, 0, verb_request },
|
||||
{ "cancel", VERB_ANY, 1, 0, verb_cancel },
|
||||
{ "complete", VERB_ANY, 1, 0, verb_complete },
|
||||
{}
|
||||
};
|
||||
|
||||
int r;
|
||||
|
||||
log_setup();
|
||||
|
||||
r = parse_argv(argc, argv);
|
||||
char **args = NULL;
|
||||
r = parse_argv(argc, argv, &args);
|
||||
if (r <= 0)
|
||||
return r;
|
||||
|
||||
if (arg_varlink)
|
||||
return varlink_service();
|
||||
|
||||
return dispatch_verb(argc, argv, verbs, /* userdata= */ NULL);
|
||||
return dispatch_verb_with_args(args, /* userdata= */ NULL);
|
||||
}
|
||||
|
||||
DEFINE_MAIN_FUNCTION_WITH_POSITIVE_FAILURE(run);
|
||||
|
||||
@@ -143,7 +143,6 @@
|
||||
|
||||
#define XCONCATENATE(x, y) x ## y
|
||||
#define CONCATENATE(x, y) XCONCATENATE(x, y)
|
||||
#define CONCATENATE3(x, y, z) CONCATENATE(x, CONCATENATE(y, z))
|
||||
|
||||
#define assert_cc(expr) _Static_assert(expr, #expr)
|
||||
|
||||
|
||||
+60
-105
@@ -1,6 +1,5 @@
|
||||
/* SPDX-License-Identifier: LGPL-2.1-or-later */
|
||||
|
||||
#include <getopt.h>
|
||||
#include <linux/vm_sockets.h>
|
||||
#include <locale.h>
|
||||
#include <string.h>
|
||||
@@ -23,6 +22,7 @@
|
||||
#include "hostname-util.h"
|
||||
#include "log.h"
|
||||
#include "main-func.h"
|
||||
#include "options.h"
|
||||
#include "parse-argument.h"
|
||||
#include "polkit-agent.h"
|
||||
#include "pretty-print.h"
|
||||
@@ -549,6 +549,7 @@ static int get_hostname_based_on_flag(sd_bus *bus) {
|
||||
return get_one_name(bus, attr, NULL);
|
||||
}
|
||||
|
||||
VERB(verb_show_status, "status", NULL, VERB_ANY, 1, VERB_DEFAULT, "Show current hostname settings");
|
||||
static int verb_show_status(int argc, char *argv[], uintptr_t _data, void *userdata) {
|
||||
sd_bus *bus = userdata;
|
||||
int r;
|
||||
@@ -687,26 +688,36 @@ static int verb_set_hostname(int argc, char *argv[], uintptr_t _data, void *user
|
||||
return ret;
|
||||
}
|
||||
|
||||
VERB(verb_get_or_set_hostname, "hostname", "[NAME]", VERB_ANY, 2, 0, "Get/set system hostname");
|
||||
VERB(verb_get_or_set_hostname, "set-hostname", "NAME", 2, 2, 0, NULL); /* obsolete */
|
||||
static int verb_get_or_set_hostname(int argc, char *argv[], uintptr_t data, void *userdata) {
|
||||
return argc == 1 ? get_hostname_based_on_flag(userdata) :
|
||||
verb_set_hostname(argc, argv, data, userdata);
|
||||
}
|
||||
|
||||
VERB(verb_get_or_set_icon_name, "icon-name", "[NAME]", VERB_ANY, 2, 0, "Get/set icon name for host");
|
||||
VERB(verb_get_or_set_icon_name, "set-icon-name", "NAME", 2, 2, 0, NULL); /* obsolete */
|
||||
static int verb_get_or_set_icon_name(int argc, char *argv[], uintptr_t _data, void *userdata) {
|
||||
return argc == 1 ? get_one_name(userdata, "IconName", NULL) :
|
||||
set_simple_string(userdata, "icon", "SetIconName", argv[1]);
|
||||
}
|
||||
|
||||
VERB(verb_get_or_set_chassis, "chassis", "[NAME]", VERB_ANY, 2, 0, "Get/set chassis type for host");
|
||||
VERB(verb_get_or_set_chassis, "set-chassis", "NAME", 2, 2, 0, NULL); /* obsolete */
|
||||
static int verb_get_or_set_chassis(int argc, char *argv[], uintptr_t _data, void *userdata) {
|
||||
return argc == 1 ? get_one_name(userdata, "Chassis", NULL) :
|
||||
set_simple_string(userdata, "chassis", "SetChassis", argv[1]);
|
||||
}
|
||||
|
||||
VERB(verb_get_or_set_deployment, "deployment", "[NAME]", VERB_ANY, 2, 0, "Get/set deployment environment for host");
|
||||
VERB(verb_get_or_set_deployment, "set-deployment", "NAME", 2, 2, 0, NULL); /* obsolete */
|
||||
static int verb_get_or_set_deployment(int argc, char *argv[], uintptr_t _data, void *userdata) {
|
||||
return argc == 1 ? get_one_name(userdata, "Deployment", NULL) :
|
||||
set_simple_string(userdata, "deployment", "SetDeployment", argv[1]);
|
||||
}
|
||||
|
||||
VERB(verb_get_or_set_location, "location", "[NAME]", VERB_ANY, 2, 0, "Get/set location for host");
|
||||
VERB(verb_get_or_set_location, "set-location", "NAME", 2, 2, 0, NULL); /* obsolete */
|
||||
static int verb_get_or_set_location(int argc, char *argv[], uintptr_t _data, void *userdata) {
|
||||
return argc == 1 ? get_one_name(userdata, "Location", NULL) :
|
||||
set_simple_string(userdata, "location", "SetLocation", argv[1]);
|
||||
@@ -714,164 +725,108 @@ static int verb_get_or_set_location(int argc, char *argv[], uintptr_t _data, voi
|
||||
|
||||
static int help(void) {
|
||||
_cleanup_free_ char *link = NULL;
|
||||
_cleanup_(table_unrefp) Table *options = NULL, *verbs = NULL;
|
||||
int r;
|
||||
|
||||
r = terminal_urlify_man("hostnamectl", "1", &link);
|
||||
if (r < 0)
|
||||
return log_oom();
|
||||
|
||||
printf("%1$s [OPTIONS...] COMMAND ...\n\n"
|
||||
"%2$sQuery or change system hostname.%3$s\n"
|
||||
"\n%4$sCommands:%5$s\n"
|
||||
" status Show current hostname settings\n"
|
||||
" hostname [NAME] Get/set system hostname\n"
|
||||
" icon-name [NAME] Get/set icon name for host\n"
|
||||
" chassis [NAME] Get/set chassis type for host\n"
|
||||
" deployment [NAME] Get/set deployment environment for host\n"
|
||||
" location [NAME] Get/set location for host\n"
|
||||
"\n%4$sOptions:%5$s\n"
|
||||
" -h --help Show this help\n"
|
||||
" --version Show package version\n"
|
||||
" --no-ask-password Do not prompt for password\n"
|
||||
" -H --host=[USER@]HOST Operate on remote host\n"
|
||||
" -M --machine=CONTAINER Operate on local container\n"
|
||||
" --transient Only set transient hostname\n"
|
||||
" --static Only set static hostname\n"
|
||||
" --pretty Only set pretty hostname\n"
|
||||
" --json=pretty|short|off\n"
|
||||
" Generate JSON output\n"
|
||||
" -j Same as --json=pretty on tty, --json=short otherwise\n"
|
||||
"\nSee the %6$s for details.\n",
|
||||
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);
|
||||
|
||||
printf("%s [OPTIONS...] COMMAND ...\n"
|
||||
"\n%sQuery or change system hostname.%s\n"
|
||||
"\nCommands:\n",
|
||||
program_invocation_short_name,
|
||||
ansi_highlight(),
|
||||
ansi_normal(),
|
||||
ansi_underline(),
|
||||
ansi_normal(),
|
||||
link);
|
||||
ansi_normal());
|
||||
table_print(verbs, stdout);
|
||||
|
||||
printf("\nOptions:\n");
|
||||
table_print(options, stdout);
|
||||
|
||||
printf("\nSee the %s for details.\n", link);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int verb_help(int argc, char *argv[], uintptr_t _data, void *userdata) {
|
||||
return help();
|
||||
}
|
||||
VERB_COMMON_HELP_HIDDEN(help);
|
||||
|
||||
static int parse_argv(int argc, char *argv[]) {
|
||||
|
||||
enum {
|
||||
ARG_VERSION = 0x100,
|
||||
ARG_NO_ASK_PASSWORD,
|
||||
ARG_TRANSIENT,
|
||||
ARG_STATIC,
|
||||
ARG_PRETTY,
|
||||
ARG_JSON,
|
||||
};
|
||||
|
||||
static const struct option options[] = {
|
||||
{ "help", no_argument, NULL, 'h' },
|
||||
{ "version", no_argument, NULL, ARG_VERSION },
|
||||
{ "transient", no_argument, NULL, ARG_TRANSIENT },
|
||||
{ "static", no_argument, NULL, ARG_STATIC },
|
||||
{ "pretty", no_argument, NULL, ARG_PRETTY },
|
||||
{ "host", required_argument, NULL, 'H' },
|
||||
{ "machine", required_argument, NULL, 'M' },
|
||||
{ "no-ask-password", no_argument, NULL, ARG_NO_ASK_PASSWORD },
|
||||
{ "json", required_argument, NULL, ARG_JSON },
|
||||
{}
|
||||
};
|
||||
|
||||
int c, r;
|
||||
static int parse_argv(int argc, char *argv[], char ***ret_args) {
|
||||
int r;
|
||||
|
||||
assert(argc >= 0);
|
||||
assert(argv);
|
||||
|
||||
while ((c = getopt_long(argc, argv, "hH:M:j", options, NULL)) >= 0)
|
||||
OptionParser state = { argc, argv };
|
||||
const char *arg;
|
||||
|
||||
FOREACH_OPTION(&state, c, &arg, /* on_error= */ return c)
|
||||
switch (c) {
|
||||
|
||||
case 'h':
|
||||
OPTION_COMMON_HELP:
|
||||
return help();
|
||||
|
||||
case ARG_VERSION:
|
||||
OPTION_COMMON_VERSION:
|
||||
return version();
|
||||
|
||||
case 'H':
|
||||
arg_transport = BUS_TRANSPORT_REMOTE;
|
||||
arg_host = optarg;
|
||||
OPTION_COMMON_NO_ASK_PASSWORD:
|
||||
arg_ask_password = false;
|
||||
break;
|
||||
|
||||
case 'M':
|
||||
r = parse_machine_argument(optarg, &arg_host, &arg_transport);
|
||||
OPTION_COMMON_HOST:
|
||||
arg_transport = BUS_TRANSPORT_REMOTE;
|
||||
arg_host = arg;
|
||||
break;
|
||||
|
||||
OPTION_COMMON_MACHINE:
|
||||
r = parse_machine_argument(arg, &arg_host, &arg_transport);
|
||||
if (r < 0)
|
||||
return r;
|
||||
break;
|
||||
|
||||
case ARG_TRANSIENT:
|
||||
OPTION_LONG("transient", NULL, "Only set transient hostname"):
|
||||
arg_transient = true;
|
||||
break;
|
||||
|
||||
case ARG_PRETTY:
|
||||
arg_pretty = true;
|
||||
break;
|
||||
|
||||
case ARG_STATIC:
|
||||
OPTION_LONG("static", NULL, "Only set static hostname"):
|
||||
arg_static = true;
|
||||
break;
|
||||
|
||||
case ARG_NO_ASK_PASSWORD:
|
||||
arg_ask_password = false;
|
||||
OPTION_LONG("pretty", NULL, "Only set pretty hostname"):
|
||||
arg_pretty = true;
|
||||
break;
|
||||
|
||||
case ARG_JSON:
|
||||
r = parse_json_argument(optarg, &arg_json_format_flags);
|
||||
OPTION_COMMON_JSON:
|
||||
r = parse_json_argument(arg, &arg_json_format_flags);
|
||||
if (r <= 0)
|
||||
return r;
|
||||
|
||||
break;
|
||||
|
||||
case 'j':
|
||||
OPTION_COMMON_LOWERCASE_J:
|
||||
arg_json_format_flags = SD_JSON_FORMAT_PRETTY_AUTO|SD_JSON_FORMAT_COLOR_AUTO;
|
||||
break;
|
||||
|
||||
case '?':
|
||||
return -EINVAL;
|
||||
|
||||
default:
|
||||
assert_not_reached();
|
||||
}
|
||||
|
||||
*ret_args = option_parser_get_args(&state);
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int hostnamectl_main(sd_bus *bus, int argc, char *argv[]) {
|
||||
|
||||
static const Verb verbs[] = {
|
||||
{ "status", VERB_ANY, 1, VERB_DEFAULT, verb_show_status },
|
||||
{ "hostname", VERB_ANY, 2, 0, verb_get_or_set_hostname },
|
||||
{ "set-hostname", 2, 2, 0, verb_get_or_set_hostname }, /* obsolete */
|
||||
{ "icon-name", VERB_ANY, 2, 0, verb_get_or_set_icon_name },
|
||||
{ "set-icon-name", 2, 2, 0, verb_get_or_set_icon_name }, /* obsolete */
|
||||
{ "chassis", VERB_ANY, 2, 0, verb_get_or_set_chassis },
|
||||
{ "set-chassis", 2, 2, 0, verb_get_or_set_chassis }, /* obsolete */
|
||||
{ "deployment", VERB_ANY, 2, 0, verb_get_or_set_deployment },
|
||||
{ "set-deployment", 2, 2, 0, verb_get_or_set_deployment }, /* obsolete */
|
||||
{ "location", VERB_ANY, 2, 0, verb_get_or_set_location },
|
||||
{ "set-location", 2, 2, 0, verb_get_or_set_location }, /* obsolete */
|
||||
{ "help", VERB_ANY, VERB_ANY, 0, verb_help }, /* Not documented, but supported since it is created. */
|
||||
{}
|
||||
};
|
||||
|
||||
return dispatch_verb(argc, argv, verbs, bus);
|
||||
}
|
||||
|
||||
static int run(int argc, char *argv[]) {
|
||||
_cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL;
|
||||
char **args = NULL;
|
||||
int r;
|
||||
|
||||
setlocale(LC_ALL, "");
|
||||
log_setup();
|
||||
|
||||
r = parse_argv(argc, argv);
|
||||
r = parse_argv(argc, argv, &args);
|
||||
if (r <= 0)
|
||||
return r;
|
||||
|
||||
@@ -879,7 +834,7 @@ static int run(int argc, char *argv[]) {
|
||||
if (r < 0)
|
||||
return bus_log_connect_error(r, arg_transport, RUNTIME_SCOPE_SYSTEM);
|
||||
|
||||
return hostnamectl_main(bus, argc, argv);
|
||||
return dispatch_verb_with_args(args, bus);
|
||||
}
|
||||
|
||||
DEFINE_MAIN_FUNCTION(run);
|
||||
|
||||
+5
-6
@@ -208,7 +208,7 @@ static int help(void) {
|
||||
return r;
|
||||
|
||||
/* Make the 1st column same width in both tables */
|
||||
(void) table_sync_column_width(options, 0, verbs, 0);
|
||||
(void) table_sync_column_widths(0, options, verbs);
|
||||
|
||||
printf("%s [OPTIONS...] COMMAND\n\n"
|
||||
"%sGenerate and print 128-bit identifiers.%s\n"
|
||||
@@ -233,10 +233,10 @@ static int parse_argv(int argc, char *argv[], char ***ret_args) {
|
||||
assert(argc >= 0);
|
||||
assert(argv);
|
||||
|
||||
OptionParser state = {};
|
||||
OptionParser state = { argc, argv };
|
||||
const char *arg;
|
||||
|
||||
FOREACH_OPTION(&state, c, argc, argv, &arg, /* on_error= */ return c)
|
||||
FOREACH_OPTION(&state, c, &arg, /* on_error= */ return c)
|
||||
switch (c) {
|
||||
OPTION_COMMON_HELP:
|
||||
return help();
|
||||
@@ -259,8 +259,7 @@ static int parse_argv(int argc, char *argv[], char ***ret_args) {
|
||||
return r;
|
||||
break;
|
||||
|
||||
OPTION_SHORT('j', NULL,
|
||||
"Equivalent to --json=pretty (on TTY) or --json=short (otherwise)"):
|
||||
OPTION_COMMON_LOWERCASE_J:
|
||||
arg_json_format_flags = SD_JSON_FORMAT_PRETTY_AUTO|SD_JSON_FORMAT_COLOR_AUTO;
|
||||
break;
|
||||
|
||||
@@ -288,7 +287,7 @@ static int parse_argv(int argc, char *argv[], char ***ret_args) {
|
||||
break;
|
||||
}
|
||||
|
||||
*ret_args = option_parser_get_args(&state, argc, argv);
|
||||
*ret_args = option_parser_get_args(&state);
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
+3
-3
@@ -158,10 +158,10 @@ static int parse_argv(int argc, char *argv[], char ***ret_args) {
|
||||
assert(argc >= 0);
|
||||
assert(argv);
|
||||
|
||||
OptionParser state = {};
|
||||
OptionParser state = { argc, argv };
|
||||
const char *arg;
|
||||
|
||||
FOREACH_OPTION(&state, c, argc, argv, &arg, /* on_error= */ return c)
|
||||
FOREACH_OPTION(&state, c, &arg, /* on_error= */ return c)
|
||||
switch (c) {
|
||||
|
||||
OPTION_COMMON_HELP:
|
||||
@@ -283,7 +283,7 @@ static int parse_argv(int argc, char *argv[], char ***ret_args) {
|
||||
|
||||
bool have_env = arg_ready || arg_stopping || arg_reloading || arg_status || pidref_is_set(&arg_pid) || !fdset_isempty(arg_fds);
|
||||
|
||||
char **args = option_parser_get_args(&state, argc, argv);
|
||||
char **args = option_parser_get_args(&state);
|
||||
|
||||
switch (arg_action) {
|
||||
|
||||
|
||||
+20
-11
@@ -2185,26 +2185,35 @@ int table_set_column_width(Table *t, size_t column, size_t width) {
|
||||
return r;
|
||||
}
|
||||
|
||||
int table_sync_column_width(Table *a, size_t column_a, Table *b, size_t column_b) {
|
||||
size_t w1, w2;
|
||||
int r;
|
||||
int _table_sync_column_widths(size_t column, Table *a, ...) {
|
||||
size_t max = 0;
|
||||
va_list ap;
|
||||
int r = 0;
|
||||
|
||||
assert(a);
|
||||
assert(b);
|
||||
|
||||
/* Make both tables have specified columns of same width */
|
||||
/* Make the specified column have the same width in the tables. */
|
||||
|
||||
r = table_data_requested_width(a, column_a, &w1);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to query table column width: %m");
|
||||
va_start(ap, a);
|
||||
for (Table *t = a; t; t = va_arg(ap, Table*)) {
|
||||
size_t w;
|
||||
|
||||
r = table_data_requested_width(b, column_b, &w2);
|
||||
r = table_data_requested_width(t, column, &w);
|
||||
if (r < 0)
|
||||
break;
|
||||
|
||||
max = MAX(max, w);
|
||||
}
|
||||
va_end(ap);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to query table column width: %m");
|
||||
|
||||
r = 0;
|
||||
RET_GATHER(r, table_set_column_width(a, column_a, MAX(w1, w2)));
|
||||
RET_GATHER(r, table_set_column_width(b, column_b, MAX(w1, w2)));
|
||||
va_start(ap, a);
|
||||
for (Table *t = a; t; t = va_arg(ap, Table*))
|
||||
RET_GATHER(r, table_set_column_width(t, column, max));
|
||||
va_end(ap);
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
|
||||
@@ -144,7 +144,8 @@ int table_hide_column_from_display_internal(Table *t, ...);
|
||||
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_width(Table *a, size_t column_a, Table *b, size_t column_b);
|
||||
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_print(Table *t, FILE *f);
|
||||
int table_format(Table *t, char **ret);
|
||||
|
||||
+38
-36
@@ -25,14 +25,6 @@ static bool option_is_metadata(const Option *opt) {
|
||||
FLAGS_SET(ASSERT_PTR(opt)->flags, OPTION_HELP_ENTRY);
|
||||
}
|
||||
|
||||
static void kill_arg(char* argv[], int argc, int index) {
|
||||
assert(index < argc);
|
||||
assert(!argv[argc]);
|
||||
|
||||
/* Eliminate argv[index] */
|
||||
memmove(argv + index, argv + index + 1, (argc - index) * sizeof(char*));
|
||||
}
|
||||
|
||||
static void shift_arg(char* argv[], int target, int source) {
|
||||
assert(argv);
|
||||
assert(target <= source);
|
||||
@@ -77,7 +69,6 @@ int option_parse(
|
||||
const Option options[],
|
||||
const Option options_end[],
|
||||
OptionParser *state,
|
||||
int argc, char *argv[],
|
||||
const Option **ret_option,
|
||||
const char **ret_arg) {
|
||||
|
||||
@@ -85,13 +76,10 @@ int option_parse(
|
||||
|
||||
/* Check and initialize */
|
||||
if (state->optind == 0) {
|
||||
if (argc < 1 || strv_isempty(argv))
|
||||
if (state->argc < 1 || strv_isempty(state->argv))
|
||||
return log_error_errno(SYNTHETIC_ERRNO(EUCLEAN), "argv cannot be empty");
|
||||
|
||||
*state = (OptionParser) {
|
||||
.optind = 1,
|
||||
.positional_offset = 1,
|
||||
};
|
||||
state->optind = state->positional_offset = 1;
|
||||
}
|
||||
|
||||
/* Look for the next option */
|
||||
@@ -104,20 +92,21 @@ int option_parse(
|
||||
if (state->short_option_offset == 0) {
|
||||
/* Skip over non-option parameters */
|
||||
for (;;) {
|
||||
if (state->optind == argc)
|
||||
if (state->optind == state->argc)
|
||||
return 0;
|
||||
|
||||
if (streq(argv[state->optind], "--")) {
|
||||
/* No more options. Eliminate "--" so that the list of positional args is clean. */
|
||||
kill_arg(argv, argc, state->optind);
|
||||
return 0;
|
||||
if (streq(state->argv[state->optind], "--")) {
|
||||
/* No more options. Move "--" before positional args so that
|
||||
* the list of positional args is clean. */
|
||||
shift_arg(state->argv, state->positional_offset++, state->optind++);
|
||||
state->parsing_stopped = true;
|
||||
}
|
||||
|
||||
if (state->parsing_stopped)
|
||||
return 0;
|
||||
|
||||
if (argv[state->optind][0] == '-' &&
|
||||
argv[state->optind][1] != '\0')
|
||||
if (state->argv[state->optind][0] == '-' &&
|
||||
state->argv[state->optind][1] != '\0')
|
||||
/* Looks like we found an option parameter */
|
||||
break;
|
||||
|
||||
@@ -126,13 +115,13 @@ int option_parse(
|
||||
|
||||
/* Find matching option entry.
|
||||
* First, figure out if we have a long option or a short option. */
|
||||
assert(argv[state->optind][0] == '-');
|
||||
assert(state->argv[state->optind][0] == '-');
|
||||
|
||||
if (argv[state->optind][1] == '-') {
|
||||
if (state->argv[state->optind][1] == '-') {
|
||||
/* We have a long option. */
|
||||
char *eq = strchr(argv[state->optind], '=');
|
||||
char *eq = strchr(state->argv[state->optind], '=');
|
||||
if (eq) {
|
||||
optname = _optname = strndup(argv[state->optind], eq - argv[state->optind]);
|
||||
optname = _optname = strndup(state->argv[state->optind], eq - state->argv[state->optind]);
|
||||
if (!_optname)
|
||||
return log_oom();
|
||||
|
||||
@@ -140,7 +129,7 @@ int option_parse(
|
||||
optval = eq + 1;
|
||||
} else
|
||||
/* argument (if any) is separate */
|
||||
optname = argv[state->optind];
|
||||
optname = state->argv[state->optind];
|
||||
|
||||
const Option *last_partial = NULL;
|
||||
unsigned n_partial_matches = 0; /* The commandline option matches a defined prefix. */
|
||||
@@ -179,7 +168,7 @@ int option_parse(
|
||||
}
|
||||
|
||||
if (state->short_option_offset > 0) {
|
||||
char optchar = argv[state->optind][state->short_option_offset];
|
||||
char optchar = state->argv[state->optind][state->short_option_offset];
|
||||
|
||||
if (asprintf(&_optname, "-%c", optchar) < 0)
|
||||
return log_oom();
|
||||
@@ -194,7 +183,7 @@ int option_parse(
|
||||
if (option_is_metadata(option) || optchar != option->short_code)
|
||||
continue;
|
||||
|
||||
const char *rest = argv[state->optind] + state->short_option_offset + 1;
|
||||
const char *rest = state->argv[state->optind] + state->short_option_offset + 1;
|
||||
|
||||
if (option_takes_arg(option) && !isempty(rest)) {
|
||||
/* The rest of this parameter is the value. */
|
||||
@@ -216,19 +205,19 @@ int option_parse(
|
||||
"%s: option '%s' doesn't allow an argument",
|
||||
program_invocation_short_name, optname);
|
||||
if (!optval && option_arg_required(option)) {
|
||||
if (!argv[state->optind + 1])
|
||||
if (!state->argv[state->optind + 1])
|
||||
return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
|
||||
"%s: option '%s' requires an argument",
|
||||
program_invocation_short_name, optname);
|
||||
optval = argv[state->optind + 1];
|
||||
optval = state->argv[state->optind + 1];
|
||||
separate_optval = true;
|
||||
}
|
||||
|
||||
if (state->short_option_offset == 0) {
|
||||
/* We're done with this option. Adjust the array and position. */
|
||||
shift_arg(argv, state->positional_offset++, state->optind++);
|
||||
shift_arg(state->argv, state->positional_offset++, state->optind++);
|
||||
if (separate_optval)
|
||||
shift_arg(argv, state->positional_offset++, state->optind++);
|
||||
shift_arg(state->argv, state->positional_offset++, state->optind++);
|
||||
}
|
||||
|
||||
if (FLAGS_SET(option->flags, OPTION_STOPS_PARSING))
|
||||
@@ -241,12 +230,25 @@ int option_parse(
|
||||
return option->id;
|
||||
}
|
||||
|
||||
char** option_parser_get_args(const OptionParser *state, int argc, char *argv[]) {
|
||||
char** option_parser_get_args(const OptionParser *state) {
|
||||
/* Returns positional args as a strv.
|
||||
* If "--" was found, it has been removed. */
|
||||
* If "--" was found, it has been moved before state->positional_offset.
|
||||
* The array is only valid, i.e. clean without any options, after parsing
|
||||
* has naturally finished. */
|
||||
|
||||
assert(state->optind > 0);
|
||||
return argv + state->positional_offset;
|
||||
assert(state->optind == state->argc || state->parsing_stopped);
|
||||
assert(state->positional_offset <= state->argc);
|
||||
|
||||
return state->argv + state->positional_offset;
|
||||
}
|
||||
|
||||
size_t option_parser_get_n_args(const OptionParser *state) {
|
||||
assert(state->optind > 0);
|
||||
assert(state->optind == state->argc || state->parsing_stopped);
|
||||
assert(state->positional_offset <= state->argc);
|
||||
|
||||
return state->argc - state->positional_offset;
|
||||
}
|
||||
|
||||
int _option_parser_get_help_table(
|
||||
@@ -264,7 +266,7 @@ int _option_parser_get_help_table(
|
||||
|
||||
bool in_group = group == NULL; /* Are we currently in the section on the array that forms
|
||||
* group <group>? The first part is the default group, so
|
||||
* the group was not specified, we are in. */
|
||||
* if the group was not specified, we are in. */
|
||||
|
||||
for (const Option *opt = options; opt < options_end; opt++) {
|
||||
bool group_marker = FLAGS_SET(opt->flags, OPTION_GROUP_MARKER);
|
||||
|
||||
+19
-10
@@ -2,7 +2,6 @@
|
||||
#pragma once
|
||||
|
||||
#include "shared-forward.h"
|
||||
#include "strv.h"
|
||||
|
||||
typedef enum OptionFlags {
|
||||
OPTION_OPTIONAL_ARG = 1U << 0, /* Same as optional_argument in getopt */
|
||||
@@ -61,14 +60,27 @@ typedef struct Option {
|
||||
OPTION_LONG("cat-config", NULL, "Show configuration files")
|
||||
#define OPTION_COMMON_TLDR \
|
||||
OPTION_LONG("tldr", NULL, "Show non-comment parts of configuration")
|
||||
#define OPTION_COMMON_NO_ASK_PASSWORD \
|
||||
OPTION_LONG("no-ask-password", NULL, "Do not prompt for password")
|
||||
#define OPTION_COMMON_HOST \
|
||||
OPTION('H', "host", "[USER@]HOST", "Operate on remote host")
|
||||
#define OPTION_COMMON_MACHINE \
|
||||
OPTION('M', "machine", "CONTAINER", "Operate on local container")
|
||||
#define OPTION_COMMON_JSON \
|
||||
OPTION_LONG("json", "FORMAT", "Generate JSON output (pretty, short, or off)")
|
||||
#define OPTION_COMMON_LOWERCASE_J \
|
||||
OPTION_SHORT('j', NULL, \
|
||||
"Equivalent to --json=pretty (on TTY) or --json=short (otherwise)")
|
||||
|
||||
/* This is magically mapped to the beginning and end of the section */
|
||||
extern const Option __start_SYSTEMD_OPTIONS[];
|
||||
extern const Option __stop_SYSTEMD_OPTIONS[];
|
||||
|
||||
typedef struct OptionParser {
|
||||
/* Those two should stay first so that it's possible to initialize the struct as { argc, argv }. */
|
||||
int argc; /* The original argc. */
|
||||
char **argv; /* The argv array, possibly reordered. */
|
||||
|
||||
int optind; /* Position of the parameter being handled.
|
||||
* 0 → option parsing hasn't been started yet. */
|
||||
int short_option_offset; /* Set when we're parsing an argument with one or more short options.
|
||||
@@ -82,25 +94,22 @@ int option_parse(
|
||||
const Option options[],
|
||||
const Option options_end[],
|
||||
OptionParser *state,
|
||||
int argc, char *argv[],
|
||||
const Option **ret_option,
|
||||
const char **ret_arg);
|
||||
|
||||
/* Iterate over options. */
|
||||
#define FOREACH_OPTION_FULL(parser, opt, argc, argv, ret_o, ret_a, on_error) \
|
||||
for (int opt; (opt = option_parse(ALIGN_PTR(__start_SYSTEMD_OPTIONS), __stop_SYSTEMD_OPTIONS, parser, argc, argv, ret_o, ret_a)) != 0; ) \
|
||||
#define FOREACH_OPTION_FULL(parser, opt, ret_o, ret_a, on_error) \
|
||||
for (int opt; (opt = option_parse(ALIGN_PTR(__start_SYSTEMD_OPTIONS), __stop_SYSTEMD_OPTIONS, parser, ret_o, ret_a)) != 0; ) \
|
||||
if (opt < 0) { \
|
||||
on_error; \
|
||||
break; \
|
||||
} else
|
||||
|
||||
#define FOREACH_OPTION(parser, opt, argc, argv, ret_a, on_error) \
|
||||
FOREACH_OPTION_FULL(parser, opt, argc, argv, /* ret_o= */ NULL, ret_a, on_error)
|
||||
#define FOREACH_OPTION(parser, opt, ret_a, on_error) \
|
||||
FOREACH_OPTION_FULL(parser, opt, /* ret_o= */ NULL, ret_a, on_error)
|
||||
|
||||
char** option_parser_get_args(const OptionParser *state, int argc, char *argv[]);
|
||||
static inline size_t option_parser_get_n_args(const OptionParser *state, int argc, char *argv[]) {
|
||||
return strv_length(option_parser_get_args(state, argc, argv));
|
||||
}
|
||||
char** option_parser_get_args(const OptionParser *state);
|
||||
size_t option_parser_get_n_args(const OptionParser *state);
|
||||
|
||||
int _option_parser_get_help_table(
|
||||
const Option options[],
|
||||
|
||||
+33
-4
@@ -58,12 +58,21 @@ bool should_bypass(const char *env_prefix) {
|
||||
return true;
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
const Verb* verbs_find_verb(const char *name, const Verb verbs[], const Verb verbs_end[]) {
|
||||
assert(verbs);
|
||||
|
||||
for (const Verb *verb = verbs; verb < verbs_end; verb++)
|
||||
for (const Verb *verb = verbs; verb < verbs_end; verb++) {
|
||||
if (verb_is_metadata(verb))
|
||||
continue;
|
||||
|
||||
if (name ? streq(name, verb->verb) : FLAGS_SET(verb->flags, VERB_DEFAULT))
|
||||
return verb;
|
||||
}
|
||||
|
||||
/* At the end of the list? */
|
||||
return NULL;
|
||||
@@ -85,6 +94,9 @@ int _dispatch_verb_with_args(char **args, const Verb verbs[], const Verb verbs_e
|
||||
_cleanup_strv_free_ char **verb_strv = NULL;
|
||||
|
||||
for (verb = verbs; verb < verbs_end; verb++) {
|
||||
if (verb_is_metadata(verb))
|
||||
continue;
|
||||
|
||||
r = strv_extend(&verb_strv, verb->verb);
|
||||
if (r < 0)
|
||||
return log_oom();
|
||||
@@ -142,13 +154,17 @@ int dispatch_verb(int argc, char *argv[], const Verb verbs[], void *userdata) {
|
||||
assert(argc >= optind);
|
||||
|
||||
size_t n = 0;
|
||||
while (verbs[n].dispatch)
|
||||
while (verbs[n].verb)
|
||||
n++;
|
||||
|
||||
return _dispatch_verb_with_args(strv_skip(argv, optind), verbs, verbs + n, userdata);
|
||||
}
|
||||
|
||||
int _verbs_get_help_table(const Verb verbs[], const Verb verbs_end[], Table **ret) {
|
||||
int _verbs_get_help_table(
|
||||
const Verb verbs[],
|
||||
const Verb verbs_end[],
|
||||
const char *group,
|
||||
Table **ret) {
|
||||
int r;
|
||||
|
||||
assert(ret);
|
||||
@@ -157,10 +173,23 @@ int _verbs_get_help_table(const Verb verbs[], const Verb verbs_end[], Table **re
|
||||
if (!table)
|
||||
return log_oom();
|
||||
|
||||
bool in_group = group == NULL; /* Are we currently in the section on the array that forms
|
||||
* 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++) {
|
||||
assert(verb->dispatch);
|
||||
assert(verb->verb);
|
||||
|
||||
bool group_marker = FLAGS_SET(verb->flags, VERB_GROUP_MARKER);
|
||||
if (!in_group) {
|
||||
in_group = group_marker && streq(group, verb->verb);
|
||||
continue;
|
||||
}
|
||||
if (group_marker)
|
||||
break; /* End of group */
|
||||
|
||||
if (!verb->help)
|
||||
/* No help string — we do not show the verb */
|
||||
continue;
|
||||
|
||||
/* We indent the option string by two spaces. We could set the minimum cell width and
|
||||
|
||||
+23
-8
@@ -8,6 +8,7 @@
|
||||
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 */
|
||||
} VerbFlags;
|
||||
|
||||
typedef struct {
|
||||
@@ -20,16 +21,13 @@ typedef struct {
|
||||
const char *help;
|
||||
} Verb;
|
||||
|
||||
#define VERB_FULL(d, v, a, amin, amax, f, dat, h) \
|
||||
DISABLE_WARNING_REDUNDANT_DECLS \
|
||||
static int d(int, char**, uintptr_t, void*); \
|
||||
REENABLE_WARNING \
|
||||
#define _VERB_DATA(d, v, a, amin, amax, f, dat, h) \
|
||||
_section_("SYSTEMD_VERBS") \
|
||||
_alignptr_ \
|
||||
_used_ \
|
||||
_retain_ \
|
||||
_variable_no_sanitize_address_ \
|
||||
static const Verb CONCATENATE3(d, _data_, __COUNTER__) = { \
|
||||
static const Verb CONCATENATE(verb_data_, __COUNTER__) = { \
|
||||
.verb = v, \
|
||||
.min_args = amin, \
|
||||
.max_args = amax, \
|
||||
@@ -40,6 +38,12 @@ typedef struct {
|
||||
.help = h, \
|
||||
}
|
||||
|
||||
#define VERB_FULL(d, v, a, amin, amax, f, dat, h) \
|
||||
DISABLE_WARNING_REDUNDANT_DECLS \
|
||||
static int d(int, char**, uintptr_t, void*); \
|
||||
REENABLE_WARNING \
|
||||
_VERB_DATA(d, v, a, amin, amax, f, dat, h)
|
||||
|
||||
/* The same as VERB_FULL, but without the data argument */
|
||||
#define VERB(d, v, a, amin, amax, f, h) \
|
||||
VERB_FULL(d, v, a, amin, amax, f, /* dat= */ 0, h)
|
||||
@@ -48,6 +52,11 @@ typedef struct {
|
||||
#define VERB_NOARG(d, v, h) \
|
||||
VERB(d, v, /* a= */ NULL, /* amin= */ VERB_ANY, /* amax= */ 1, /* f= */ 0, h)
|
||||
|
||||
/* Magic entry in the table (which will not be returned) that designates the start of the group <gr>. */
|
||||
#define VERB_GROUP(gr) \
|
||||
_VERB_DATA(/* d= */ NULL, /* v= */ gr, /* a= */ NULL, /* amin= */ 0, /* amax= */ 0, \
|
||||
/* f= */ VERB_GROUP_MARKER, /* dat= */ 0, /* h= */ NULL)
|
||||
|
||||
/* This is magically mapped to the beginning and end of the section */
|
||||
extern const Verb __start_SYSTEMD_VERBS[];
|
||||
extern const Verb __stop_SYSTEMD_VERBS[];
|
||||
@@ -64,9 +73,15 @@ int _dispatch_verb_with_args(char **args, const Verb verbs[], const Verb verbs_e
|
||||
|
||||
int dispatch_verb(int argc, char *argv[], const Verb verbs[], void *userdata);
|
||||
|
||||
int _verbs_get_help_table(const Verb verbs[], const Verb verbs_end[], Table **ret);
|
||||
#define verbs_get_help_table(ret) \
|
||||
_verbs_get_help_table(ALIGN_PTR(__start_SYSTEMD_VERBS), __stop_SYSTEMD_VERBS, ret)
|
||||
int _verbs_get_help_table(
|
||||
const Verb verbs[],
|
||||
const Verb verbs_end[],
|
||||
const char *group,
|
||||
Table **ret);
|
||||
#define verbs_get_help_table_group(group, ret) \
|
||||
_verbs_get_help_table(ALIGN_PTR(__start_SYSTEMD_VERBS), __stop_SYSTEMD_VERBS, group, ret)
|
||||
#define verbs_get_help_table(ret) \
|
||||
verbs_get_help_table_group(/* group= */ NULL, ret)
|
||||
|
||||
#define _VERB_COMMON_HELP_IMPL(impl) \
|
||||
static int verb_help(int argc, char **argv, uintptr_t data, void *userdata) { \
|
||||
|
||||
@@ -31,10 +31,10 @@ static void test_option_parse_one(
|
||||
for (const Entry *e = entries; e && (e->long_code || e->short_code != 0); e++)
|
||||
n_entries++;
|
||||
|
||||
OptionParser state = {};
|
||||
OptionParser state = { argc, argv };
|
||||
const Option *opt;
|
||||
const char *arg;
|
||||
for (int c; (c = option_parse(options, options + n_options, &state, argc, argv, &opt, &arg)) != 0; ) {
|
||||
for (int c; (c = option_parse(options, options + n_options, &state, &opt, &arg)) != 0; ) {
|
||||
ASSERT_OK(c);
|
||||
ASSERT_NOT_NULL(opt);
|
||||
|
||||
@@ -54,11 +54,11 @@ static void test_option_parse_one(
|
||||
|
||||
ASSERT_EQ(i, n_entries);
|
||||
|
||||
char **args = option_parser_get_args(&state, argc, argv);
|
||||
char **args = option_parser_get_args(&state);
|
||||
ASSERT_TRUE(strv_equal(args, remaining));
|
||||
ASSERT_STREQ(argv[0], saved_argv0);
|
||||
|
||||
ASSERT_EQ(option_parser_get_n_args(&state, argc, argv), strv_length(remaining));
|
||||
ASSERT_EQ(option_parser_get_n_args(&state), strv_length(remaining));
|
||||
}
|
||||
|
||||
static void test_option_invalid_one(
|
||||
@@ -77,11 +77,11 @@ static void test_option_invalid_one(
|
||||
for (const Option *o = options; o->short_code != 0 || o->long_code; o++)
|
||||
n_options++;
|
||||
|
||||
OptionParser state = {};
|
||||
OptionParser state = { argc, argv };
|
||||
const Option *opt;
|
||||
const char *arg;
|
||||
|
||||
int c = option_parse(options, options + n_options, &state, argc, argv, &opt, &arg);
|
||||
int c = option_parse(options, options + n_options, &state, &opt, &arg);
|
||||
ASSERT_ERROR(c, EINVAL);
|
||||
}
|
||||
|
||||
@@ -691,11 +691,11 @@ static void test_macros_parse_one(
|
||||
for (const Entry *e = entries; e && (e->long_code || e->short_code != 0); e++)
|
||||
n_entries++;
|
||||
|
||||
OptionParser state = {};
|
||||
OptionParser state = { argc, argv };
|
||||
const Option *opt;
|
||||
const char *arg;
|
||||
|
||||
FOREACH_OPTION_FULL(&state, c, argc, argv, &opt, &arg, ASSERT_TRUE(false)) {
|
||||
FOREACH_OPTION_FULL(&state, c, &opt, &arg, ASSERT_TRUE(false)) {
|
||||
log_debug("%c %s: %s=%s",
|
||||
opt->short_code != 0 ? opt->short_code : ' ',
|
||||
opt->long_code ?: "",
|
||||
@@ -751,7 +751,7 @@ static void test_macros_parse_one(
|
||||
|
||||
ASSERT_EQ(i, n_entries);
|
||||
|
||||
char **args = option_parser_get_args(&state, argc, argv);
|
||||
char **args = option_parser_get_args(&state);
|
||||
ASSERT_TRUE(strv_equal(args, remaining));
|
||||
ASSERT_STREQ(argv[0], saved_argv0);
|
||||
}
|
||||
|
||||
+26
-16
@@ -16,14 +16,16 @@ static int noop_dispatcher(int argc, char *argv[], uintptr_t _data, void *userda
|
||||
|
||||
TEST(verbs) {
|
||||
static const Verb verbs[] = {
|
||||
{ "help", VERB_ANY, VERB_ANY, 0, noop_dispatcher },
|
||||
{ "list-images", VERB_ANY, 1, 0, noop_dispatcher },
|
||||
{ "list", VERB_ANY, 2, VERB_DEFAULT, noop_dispatcher },
|
||||
{ "status", 2, VERB_ANY, 0, noop_dispatcher },
|
||||
{ "show", VERB_ANY, VERB_ANY, 0, noop_dispatcher },
|
||||
{ "terminate", 2, VERB_ANY, 0, noop_dispatcher },
|
||||
{ "login", 2, 2, 0, noop_dispatcher },
|
||||
{ "copy-to", 3, 4, 0, noop_dispatcher },
|
||||
{ "help", VERB_ANY, VERB_ANY, 0, noop_dispatcher },
|
||||
{ "list-images", VERB_ANY, 1, 0, noop_dispatcher },
|
||||
{ "list", VERB_ANY, 2, VERB_DEFAULT, noop_dispatcher },
|
||||
{ "status", 2, VERB_ANY, 0, noop_dispatcher },
|
||||
{ "Group2", VERB_ANY, VERB_ANY, VERB_GROUP_MARKER, NULL },
|
||||
{ "show", VERB_ANY, VERB_ANY, 0, noop_dispatcher },
|
||||
{ "terminate", 2, VERB_ANY, 0, noop_dispatcher },
|
||||
{ "Group3", 0, 0, VERB_GROUP_MARKER, NULL },
|
||||
{ "login", 2, 2, 0, noop_dispatcher },
|
||||
{ "copy-to", 3, 4, 0, noop_dispatcher },
|
||||
{}
|
||||
};
|
||||
|
||||
@@ -44,6 +46,12 @@ TEST(verbs) {
|
||||
|
||||
/* no verb, but a default is set */
|
||||
test_dispatch_one(STRV_EMPTY, verbs, 0);
|
||||
|
||||
/* the group entry shall not be found */
|
||||
test_dispatch_one(STRV_MAKE("Group2"), verbs, -EINVAL);
|
||||
|
||||
/* the group entry shall not be found */
|
||||
test_dispatch_one(STRV_MAKE("Group3"), verbs, -EINVAL);
|
||||
}
|
||||
|
||||
TEST(verbs_no_default) {
|
||||
@@ -60,14 +68,15 @@ TEST(verbs_no_default) {
|
||||
|
||||
TEST(verbs_no_default_many) {
|
||||
static const Verb verbs[] = {
|
||||
{ "help", VERB_ANY, VERB_ANY, 0, noop_dispatcher },
|
||||
{ "list-images", VERB_ANY, 1, 0, noop_dispatcher },
|
||||
{ "list", VERB_ANY, 2, 0, noop_dispatcher },
|
||||
{ "status", 2, VERB_ANY, 0, noop_dispatcher },
|
||||
{ "show", VERB_ANY, VERB_ANY, 0, noop_dispatcher },
|
||||
{ "terminate", 2, VERB_ANY, 0, noop_dispatcher },
|
||||
{ "login", 2, 2, 0, noop_dispatcher },
|
||||
{ "copy-to", 3, 4, 0, noop_dispatcher },
|
||||
{ "help", VERB_ANY, VERB_ANY, 0, noop_dispatcher },
|
||||
{ "list-images", VERB_ANY, 1, 0, noop_dispatcher },
|
||||
{ "list", VERB_ANY, 2, 0, noop_dispatcher },
|
||||
{ "status", 2, VERB_ANY, 0, noop_dispatcher },
|
||||
{ "Specials", VERB_ANY, VERB_ANY, VERB_GROUP_MARKER, NULL },
|
||||
{ "show", VERB_ANY, VERB_ANY, 0, noop_dispatcher },
|
||||
{ "terminate", 2, VERB_ANY, 0, noop_dispatcher },
|
||||
{ "login", 2, 2, 0, noop_dispatcher },
|
||||
{ "copy-to", 3, 4, 0, noop_dispatcher },
|
||||
{}
|
||||
};
|
||||
|
||||
@@ -75,6 +84,7 @@ TEST(verbs_no_default_many) {
|
||||
test_dispatch_one(STRV_MAKE("hel"), verbs, -EINVAL);
|
||||
test_dispatch_one(STRV_MAKE("helpp"), verbs, -EINVAL);
|
||||
test_dispatch_one(STRV_MAKE("hgrejgoraoiosafso"), verbs, -EINVAL);
|
||||
test_dispatch_one(STRV_MAKE("Specials"), verbs, -EINVAL);
|
||||
}
|
||||
|
||||
DEFINE_TEST_MAIN(LOG_INFO);
|
||||
|
||||
@@ -94,10 +94,10 @@ static int parse_argv(int argc, char *argv[]) {
|
||||
assert(argc >= 0);
|
||||
assert(argv);
|
||||
|
||||
OptionParser state = {};
|
||||
OptionParser state = { argc, argv };
|
||||
const char *arg;
|
||||
|
||||
FOREACH_OPTION(&state, c, argc, argv, &arg, /* on_error= */ return c)
|
||||
FOREACH_OPTION(&state, c, &arg, /* on_error= */ return c)
|
||||
switch (c) {
|
||||
OPTION_COMMON_HELP:
|
||||
return help();
|
||||
@@ -112,7 +112,7 @@ static int parse_argv(int argc, char *argv[]) {
|
||||
break;
|
||||
}
|
||||
|
||||
if (option_parser_get_n_args(&state, argc, argv) > 0)
|
||||
if (option_parser_get_n_args(&state) > 0)
|
||||
return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "This program takes no arguments.");
|
||||
|
||||
return 1;
|
||||
|
||||
@@ -62,10 +62,10 @@ static int parse_argv(int argc, char *argv[]) {
|
||||
assert(argc >= 0);
|
||||
assert(argv);
|
||||
|
||||
OptionParser state = {};
|
||||
OptionParser state = { argc, argv };
|
||||
const char *arg;
|
||||
|
||||
FOREACH_OPTION(&state, c, argc, argv, &arg, /* on_error= */ return c)
|
||||
FOREACH_OPTION(&state, c, &arg, /* on_error= */ return c)
|
||||
switch (c) {
|
||||
OPTION_COMMON_HELP:
|
||||
return help();
|
||||
@@ -88,7 +88,7 @@ static int parse_argv(int argc, char *argv[]) {
|
||||
break;
|
||||
}
|
||||
|
||||
char **args = option_parser_get_args(&state, argc, argv);
|
||||
char **args = option_parser_get_args(&state);
|
||||
|
||||
if (strv_length(args) != 1)
|
||||
return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
|
||||
|
||||
Reference in New Issue
Block a user