diff --git a/man/varlinkctl.xml b/man/varlinkctl.xml
index 5a3e1d0050..0dedc1e4db 100644
--- a/man/varlinkctl.xml
+++ b/man/varlinkctl.xml
@@ -119,6 +119,17 @@
+
+ list-methods ADDRESS [INTERFACE…]
+
+ 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.
+
+
+
+
introspect ADDRESS [INTERFACE…]
diff --git a/src/varlinkctl/varlinkctl.c b/src/varlinkctl/varlinkctl.c
index 6d9a6a9721..739770d296 100644
--- a/src/varlinkctl/varlinkctl.c
+++ b/src/varlinkctl/varlinkctl.c
@@ -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 },