varlinkctl: add new list-methods verb

For putting together "varlinkctl call" command lines it's useful to
quickly enumerate all methods implemented by a service. Hence, let's add
a new "list-methods" which uses the introspection data of a service to
quickly list methods.

This is implemented as a special flavour of the "introspect" logic,
and just suppresses all output except for the method names.
This commit is contained in:
Lennart Poettering 2024-05-24 16:34:12 +02:00
parent 2475b0e81a
commit 16cfe84c24
2 changed files with 57 additions and 6 deletions

View File

@ -119,6 +119,17 @@
<xi:include href="version-info.xml" xpointer="v255"/></listitem>
</varlistentry>
<varlistentry>
<term><command>list-methods</command> <replaceable>ADDRESS</replaceable> [<replaceable>INTERFACE…</replaceable>]</term>
<listitem><para>Show list of methods implemented by the specified service. Expects a service address
in one of the formats described above as well as one or more interface names. If no interface name is
specified, lists all methods of all interfaces implemented by the service, otherwise just the methods
in the specified services.</para>
<xi:include href="version-info.xml" xpointer="v257"/></listitem>
</varlistentry>
<varlistentry>
<term><command>introspect</command> <replaceable>ADDRESS</replaceable> [<replaceable>INTERFACE…</replaceable>]</term>

View File

@ -38,6 +38,9 @@ static int help(void) {
" info ADDRESS Show service information\n"
" list-interfaces ADDRESS\n"
" List interfaces implemented by service\n"
" list-methods ADDRESS [INTERFACE…]\n"
" List methods implemented by services or specific\n"
" interfaces\n"
" introspect ADDRESS [INTERFACE…]\n"
" Show interface definition\n"
" call ADDRESS METHOD [PARAMS]\n"
@ -293,9 +296,11 @@ static int verb_introspect(int argc, char *argv[], void *userdata) {
_cleanup_strv_free_ char **auto_interfaces = NULL;
char **interfaces;
const char *url;
bool list_methods;
int r;
assert(argc >= 2);
list_methods = streq(argv[0], "list-methods");
url = argv[1];
interfaces = strv_skip(argv, 2);
@ -328,9 +333,11 @@ static int verb_introspect(int argc, char *argv[], void *userdata) {
}
/* Automatically switch on JSON_SEQ if we output multiple JSON objects */
if (strv_length(interfaces) > 1)
if (!list_methods && strv_length(interfaces) > 1)
arg_json_format_flags |= SD_JSON_FORMAT_SEQ;
_cleanup_strv_free_ char **methods = NULL;
STRV_FOREACH(i, interfaces) {
sd_json_variant *reply = NULL;
r = varlink_callb_and_log(
@ -341,9 +348,7 @@ static int verb_introspect(int argc, char *argv[], void *userdata) {
if (r < 0)
return r;
pager_open(arg_pager_flags);
if (FLAGS_SET(arg_json_format_flags, SD_JSON_FORMAT_OFF)) {
if (FLAGS_SET(arg_json_format_flags, SD_JSON_FORMAT_OFF) || list_methods) {
static const struct sd_json_dispatch_field dispatch_table[] = {
{ "description", SD_JSON_VARIANT_STRING, sd_json_dispatch_const_string, 0, SD_JSON_MANDATORY },
{}
@ -356,22 +361,56 @@ static int verb_introspect(int argc, char *argv[], void *userdata) {
if (r < 0)
return r;
if (i > interfaces)
if (!list_methods && i > interfaces)
print_separator();
/* Try to parse the returned description, so that we can add syntax highlighting */
r = varlink_idl_parse(ASSERT_PTR(description), &line, &column, &vi);
if (r < 0) {
if (list_methods)
return log_error_errno(r, "Failed to parse returned interface description at %u:%u: %m", line, column);
log_warning_errno(r, "Failed to parse returned interface description at %u:%u, showing raw interface description: %m", line, column);
pager_open(arg_pager_flags);
fputs_with_newline(description, stdout);
} else if (list_methods) {
for (const VarlinkSymbol *const *y = vi->symbols, *symbol; (symbol = *y); y++) {
if (symbol->symbol_type != VARLINK_METHOD)
continue;
r = strv_extendf(&methods, "%s.%s", vi->name, symbol->name);
if (r < 0)
return log_oom();
}
} else {
pager_open(arg_pager_flags);
r = varlink_idl_dump(stdout, /* use_colors= */ -1, vi);
if (r < 0)
return log_error_errno(r, "Failed to format parsed interface description: %m");
}
} else
} else {
pager_open(arg_pager_flags);
sd_json_variant_dump(reply, arg_json_format_flags, stdout, NULL);
}
}
if (list_methods) {
pager_open(arg_pager_flags);
strv_sort(strv_uniq(methods));
if (FLAGS_SET(arg_json_format_flags, SD_JSON_FORMAT_OFF))
strv_print(methods);
else {
_cleanup_(sd_json_variant_unrefp) sd_json_variant *j = NULL;
r = sd_json_build(&j, SD_JSON_BUILD_STRV(methods));
if (r < 0)
return log_error_errno(r, "Failed to build JSON array: %m");
sd_json_variant_dump(j, arg_json_format_flags, stdout, NULL);
}
}
return 0;
@ -573,6 +612,7 @@ static int varlinkctl_main(int argc, char *argv[]) {
{ "info", 2, 2, 0, verb_info },
{ "list-interfaces", 2, 2, 0, verb_info },
{ "introspect", 2, VERB_ANY, 0, verb_introspect },
{ "list-methods", 2, VERB_ANY, 0, verb_introspect },
{ "call", 3, 4, 0, verb_call },
{ "validate-idl", 1, 2, 0, verb_validate_idl },
{ "help", VERB_ANY, VERB_ANY, 0, verb_help },