verbs: add a section to list verbs similarly to options
This commit is contained in:
+52
-9
@@ -3,6 +3,7 @@
|
||||
#include <getopt.h>
|
||||
|
||||
#include "env-util.h"
|
||||
#include "format-table.h"
|
||||
#include "log.h"
|
||||
#include "string-util.h"
|
||||
#include "strv.h"
|
||||
@@ -57,33 +58,34 @@ bool should_bypass(const char *env_prefix) {
|
||||
return true;
|
||||
}
|
||||
|
||||
const Verb* verbs_find_verb(const char *name, const Verb verbs[]) {
|
||||
const Verb* verbs_find_verb(const char *name, const Verb verbs[], const Verb verbs_end[]) {
|
||||
assert(verbs);
|
||||
|
||||
for (size_t i = 0; verbs[i].dispatch; i++)
|
||||
if (name ? streq(name, verbs[i].verb) : FLAGS_SET(verbs[i].flags, VERB_DEFAULT))
|
||||
return verbs + i;
|
||||
for (const Verb *verb = verbs; verb < verbs_end; verb++)
|
||||
if (name ? streq(name, verb->verb) : FLAGS_SET(verb->flags, VERB_DEFAULT))
|
||||
return verb;
|
||||
|
||||
/* At the end of the list? */
|
||||
return NULL;
|
||||
}
|
||||
|
||||
int dispatch_verb_with_args(char **args, const Verb verbs[], void *userdata) {
|
||||
int _dispatch_verb_with_args(char **args, const Verb verbs[], const Verb verbs_end[], void *userdata) {
|
||||
int r;
|
||||
|
||||
assert(verbs);
|
||||
assert(verbs_end > verbs);
|
||||
assert(verbs[0].dispatch);
|
||||
assert(verbs[0].verb);
|
||||
|
||||
const char *name = args ? args[0] : NULL;
|
||||
size_t left = strv_length(args);
|
||||
|
||||
const Verb *verb = verbs_find_verb(name, verbs);
|
||||
const Verb *verb = verbs_find_verb(name, verbs, verbs_end);
|
||||
if (!verb) {
|
||||
_cleanup_strv_free_ char **verb_strv = NULL;
|
||||
|
||||
for (size_t i = 0; verbs[i].dispatch; i++) {
|
||||
r = strv_extend(&verb_strv, verbs[i].verb);
|
||||
for (verb = verbs; verb < verbs_end; verb++) {
|
||||
r = strv_extend(&verb_strv, verb->verb);
|
||||
if (r < 0)
|
||||
return log_oom();
|
||||
}
|
||||
@@ -139,5 +141,46 @@ int dispatch_verb(int argc, char *argv[], const Verb verbs[], void *userdata) {
|
||||
assert(argv);
|
||||
assert(argc >= optind);
|
||||
|
||||
return dispatch_verb_with_args(strv_skip(argv, optind), verbs, userdata);
|
||||
size_t n = 0;
|
||||
while (verbs[n].dispatch)
|
||||
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 r;
|
||||
|
||||
assert(ret);
|
||||
|
||||
_cleanup_(table_unrefp) Table *table = table_new("verb", "help");
|
||||
if (!table)
|
||||
return log_oom();
|
||||
|
||||
for (const Verb *verb = verbs; verb < verbs_end; verb++) {
|
||||
assert(verb->dispatch);
|
||||
|
||||
/* 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. */
|
||||
r = table_add_cell_stringf(table, NULL, " %s%s%s",
|
||||
verb->verb,
|
||||
verb->argspec ? " " : "",
|
||||
strempty(verb->argspec));
|
||||
if (r < 0)
|
||||
return table_log_add_error(r);
|
||||
|
||||
const char *help = verb->help ?: "FIXME";
|
||||
_cleanup_strv_free_ char **s = strv_split(help, /* separators= */ NULL);
|
||||
if (!s)
|
||||
return log_oom();
|
||||
|
||||
r = table_add_many(table, TABLE_STRV_WRAPPED, s);
|
||||
if (r < 0)
|
||||
return table_log_add_error(r);
|
||||
}
|
||||
|
||||
table_set_header(table, false);
|
||||
*ret = TAKE_PTR(table);
|
||||
return 0;
|
||||
}
|
||||
|
||||
+48
-2
@@ -16,12 +16,58 @@ typedef struct {
|
||||
VerbFlags flags;
|
||||
int (* const dispatch)(int argc, char *argv[], uintptr_t data, void *userdata);
|
||||
uintptr_t data;
|
||||
const char *argspec;
|
||||
const char *help;
|
||||
} Verb;
|
||||
|
||||
#define VERB_FULL(d, v, a, amin, amax, f, dat, h) \
|
||||
static int d(int, char**, uintptr_t, void*); \
|
||||
_section_("SYSTEMD_VERBS") \
|
||||
_alignptr_ \
|
||||
_used_ \
|
||||
_retain_ \
|
||||
_variable_no_sanitize_address_ \
|
||||
static const Verb CONCATENATE(d, _data) = { \
|
||||
.verb = v, \
|
||||
.min_args = amin, \
|
||||
.max_args = amax, \
|
||||
.flags = f, \
|
||||
.dispatch = d, \
|
||||
.data = dat, \
|
||||
.argspec = a, \
|
||||
.help = 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)
|
||||
|
||||
/* Simplified VERB for parameters that take no argument */
|
||||
#define VERB_NOARG(d, v, h) \
|
||||
VERB(d, v, /* a= */ NULL, /* amin= */ VERB_ANY, /* amax= */ 1, /* f= */ 0, h)
|
||||
|
||||
/* This is magically mapped to the beginning and end of the section */
|
||||
extern const Verb __start_SYSTEMD_VERBS[];
|
||||
extern const Verb __stop_SYSTEMD_VERBS[];
|
||||
|
||||
bool running_in_chroot_or_offline(void);
|
||||
|
||||
bool should_bypass(const char *env_prefix);
|
||||
|
||||
const Verb* verbs_find_verb(const char *name, const Verb verbs[]);
|
||||
int dispatch_verb_with_args(char **args, const Verb verbs[], void *userdata);
|
||||
const Verb* verbs_find_verb(const char *name, const Verb verbs[], const Verb verbs_end[]);
|
||||
|
||||
int _dispatch_verb_with_args(char **args, const Verb verbs[], const Verb verbs_end[], void *userdata);
|
||||
#define dispatch_verb_with_args(args, userdata) \
|
||||
_dispatch_verb_with_args(args, ALIGN_PTR(__start_SYSTEMD_VERBS), __stop_SYSTEMD_VERBS, userdata)
|
||||
|
||||
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)
|
||||
|
||||
#define VERB_COMMON_HELP(impl) \
|
||||
VERB(verb_help, "help", NULL, VERB_ANY, VERB_ANY, 0, "Show this help"); \
|
||||
static int verb_help(int argc, char **argv, uintptr_t data, void *userdata) { \
|
||||
return impl(); \
|
||||
}
|
||||
|
||||
@@ -135,7 +135,7 @@ static int systemctl_main(int argc, char *argv[]) {
|
||||
{}
|
||||
};
|
||||
|
||||
const Verb *verb = verbs_find_verb(argv[optind], verbs);
|
||||
const Verb *verb = verbs_find_verb(argv[optind], verbs, verbs + ELEMENTSOF(verbs) - 1);
|
||||
if (verb && (verb->flags & VERB_ONLINE_ONLY) && arg_root)
|
||||
return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
|
||||
"Verb '%s' cannot be used with --root= or --image=.",
|
||||
|
||||
Reference in New Issue
Block a user