1
0
mirror of https://github.com/systemd/systemd synced 2024-07-05 17:39:42 +00:00

Merge pull request #33012 from poettering/varlinkctl-list-methods

varlinkctl: make interface parameter for "varlinkctl introspect" optional, and add "list-methods" verb
This commit is contained in:
Lennart Poettering 2024-06-13 11:05:07 +02:00 committed by GitHub
commit 639256f380
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
7 changed files with 172 additions and 48 deletions

View File

@ -41,7 +41,7 @@
<arg choice="opt" rep="repeat">OPTIONS</arg>
<arg choice="plain">introspect</arg>
<arg choice="plain"><replaceable>ADDRESS</replaceable></arg>
<arg choice="plain"><replaceable>INTERFACE</replaceable></arg>
<arg choice="opt"><replaceable>INTERFACE</replaceable></arg>
</cmdsynopsis>
<cmdsynopsis>
@ -120,11 +120,23 @@
</varlistentry>
<varlistentry>
<term><command>introspect</command> <replaceable>ADDRESS</replaceable> <replaceable>INTERFACE</replaceable></term>
<term><command>list-methods</command> <replaceable>ADDRESS</replaceable> [<replaceable>INTERFACE</replaceable>]</term>
<listitem><para>Show interface definition of the specified interface provided by the specified
service. Expects a service address in one of the formats described above and a Varlink interface
name.</para>
<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>
<listitem><para>Show the interface definitions of the specified interfaces provided by the specified
service. Expects a service address in one of the formats described above and optionally one or more
Varlink interface names. If no interface names are specified, shows all provided interfaces by the
service.</para>
<xi:include href="version-info.xml" xpointer="v255"/></listitem>
</varlistentry>

View File

@ -306,3 +306,19 @@ ssize_t sparse_write(int fd, const void *p, size_t sz, size_t run_length) {
return q - (const uint8_t*) p;
}
int fputs_with_newline(const char *s, FILE *f) {
assert(s);
assert(f);
/* This is like fputs() but outputs a trailing newline char, but only if the string doesn't end in a
* newline anyway. Just like fputs() returns EOF on error. Otherwise returns 0 in case we didn't
* append a newline, > 0 otherwise. */
if (fputs(s, f) == EOF)
return EOF;
if (endswith(s, "\n"))
return 0;
return fputc('\n', f) == EOF ? EOF : 1;
}

View File

@ -5,6 +5,7 @@
#include <stdbool.h>
#include <stddef.h>
#include <stdint.h>
#include <stdio.h>
#include <sys/types.h>
#include "macro.h"
@ -44,3 +45,5 @@ static inline bool FILE_SIZE_VALID_OR_INFINITY(uint64_t l) {
return FILE_SIZE_VALID(l);
}
int fputs_with_newline(const char *s, FILE *f);

View File

@ -33,6 +33,7 @@
#include "format-util.h"
#include "id128-util.h"
#include "install.h"
#include "io-util.h"
#include "iovec-util.h"
#include "label-util.h"
#include "load-dropin.h"
@ -4569,10 +4570,7 @@ int unit_write_setting(Unit *u, UnitWriteFlags flags, const char *name, const ch
if (u->transient_file) {
/* When this is a transient unit file in creation, then let's not create a new drop-in but instead
* write to the transient unit file. */
fputs(data, u->transient_file);
if (!endswith(data, "\n"))
fputc('\n', u->transient_file);
fputs_with_newline(data, u->transient_file);
/* Remember which section we wrote this entry to */
u->last_section_private = !!(flags & UNIT_PRIVATE);

View File

@ -289,7 +289,7 @@ void print_separator(void) {
size_t c = columns();
flockfile(stdout);
fputs_unlocked(ANSI_UNDERLINE, stdout);
fputs_unlocked(ANSI_GREY_UNDERLINE, stdout);
for (size_t i = 0; i < c; i++)
fputc_unlocked(' ', stdout);

View File

@ -6,6 +6,7 @@
#include "fd-util.h"
#include "fileio.h"
#include "format-table.h"
#include "io-util.h"
#include "main-func.h"
#include "pager.h"
#include "parse-argument.h"
@ -37,7 +38,10 @@ static int help(void) {
" info ADDRESS Show service information\n"
" list-interfaces ADDRESS\n"
" List interfaces implemented by service\n"
" introspect ADDRESS INTERFACE\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"
" Invoke method\n"
@ -235,7 +239,7 @@ static int verb_info(int argc, char *argv[], void *userdata) {
};
_cleanup_(get_info_data_done) GetInfoData data = {};
r = sd_json_dispatch(reply, dispatch_table, SD_JSON_LOG, &data);
r = sd_json_dispatch(reply, dispatch_table, SD_JSON_LOG|SD_JSON_ALLOW_EXTENSIONS, &data);
if (r < 0)
return r;
@ -289,56 +293,125 @@ typedef struct GetInterfaceDescriptionData {
static int verb_introspect(int argc, char *argv[], void *userdata) {
_cleanup_(varlink_unrefp) Varlink *vl = NULL;
const char *url, *interface;
_cleanup_strv_free_ char **auto_interfaces = NULL;
char **interfaces;
const char *url;
bool list_methods;
int r;
assert(argc == 3);
assert(argc >= 2);
list_methods = streq(argv[0], "list-methods");
url = argv[1];
interface = argv[2];
interfaces = strv_skip(argv, 2);
r = varlink_connect_auto(&vl, url);
if (r < 0)
return r;
sd_json_variant *reply = NULL;
r = varlink_callb_and_log(
vl,
"org.varlink.service.GetInterfaceDescription",
&reply,
SD_JSON_BUILD_OBJECT(SD_JSON_BUILD_PAIR_STRING("interface", interface)));
if (r < 0)
return r;
if (strv_isempty(interfaces)) {
sd_json_variant *reply = NULL;
pager_open(arg_pager_flags);
/* If no interface is specified, introspect all of them */
if (FLAGS_SET(arg_json_format_flags, SD_JSON_FORMAT_OFF)) {
static const struct sd_json_dispatch_field dispatch_table[] = {
{ "description", SD_JSON_VARIANT_STRING, sd_json_dispatch_const_string, 0, SD_JSON_MANDATORY },
{}
};
_cleanup_(varlink_interface_freep) VarlinkInterface *vi = NULL;
const char *description = NULL;
unsigned line = 0, column = 0;
r = sd_json_dispatch(reply, dispatch_table, SD_JSON_LOG, &description);
r = varlink_call_and_log(vl, "org.varlink.service.GetInfo", /* parameters= */ NULL, &reply);
if (r < 0)
return r;
/* 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) {
log_warning_errno(r, "Failed to parse returned interface description at %u:%u, showing raw interface description: %m", line, column);
const struct sd_json_dispatch_field dispatch_table[] = {
{ "interfaces", SD_JSON_VARIANT_ARRAY, sd_json_dispatch_strv, PTR_TO_SIZE(&auto_interfaces), SD_JSON_MANDATORY },
{}
};
fputs(description, stdout);
if (!endswith(description, "\n"))
fputs("\n", stdout);
} else {
r = varlink_idl_dump(stdout, /* use_colors= */ -1, vi);
r = sd_json_dispatch(reply, dispatch_table, SD_JSON_LOG|SD_JSON_ALLOW_EXTENSIONS, NULL);
if (r < 0)
return r;
if (strv_isempty(auto_interfaces))
return log_error_errno(SYNTHETIC_ERRNO(ENXIO), "Service doesn't report any implemented interfaces.");
interfaces = strv_sort(strv_uniq(auto_interfaces));
}
/* Automatically switch on JSON_SEQ if we output multiple JSON objects */
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(
vl,
"org.varlink.service.GetInterfaceDescription",
&reply,
SD_JSON_BUILD_OBJECT(SD_JSON_BUILD_PAIR_STRING("interface", *i)));
if (r < 0)
return r;
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 },
{}
};
_cleanup_(varlink_interface_freep) VarlinkInterface *vi = NULL;
const char *description = NULL;
unsigned line = 0, column = 0;
r = sd_json_dispatch(reply, dispatch_table, SD_JSON_LOG|SD_JSON_ALLOW_EXTENSIONS, &description);
if (r < 0)
return log_error_errno(r, "Failed to format parsed interface description: %m");
return r;
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 {
pager_open(arg_pager_flags);
sd_json_variant_dump(reply, arg_json_format_flags, stdout, NULL);
}
} else
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;
}
@ -538,7 +611,8 @@ static int varlinkctl_main(int argc, char *argv[]) {
static const Verb verbs[] = {
{ "info", 2, 2, 0, verb_info },
{ "list-interfaces", 2, 2, 0, verb_info },
{ "introspect", 3, 3, 0, verb_introspect },
{ "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 },

View File

@ -25,6 +25,15 @@ varlinkctl info -j /run/systemd/journal/io.systemd.journal | jq .
varlinkctl list-interfaces /run/systemd/journal/io.systemd.journal
varlinkctl list-interfaces -j /run/systemd/journal/io.systemd.journal | jq .
varlinkctl list-methods /run/systemd/journal/io.systemd.journal
varlinkctl list-methods -j /run/systemd/journal/io.systemd.journal | jq .
varlinkctl list-methods /run/systemd/journal/io.systemd.journal io.systemd.Journal
varlinkctl list-methods -j /run/systemd/journal/io.systemd.journal io.systemd.Journal | jq .
varlinkctl introspect /run/systemd/journal/io.systemd.journal
varlinkctl introspect -j /run/systemd/journal/io.systemd.journal | jq --seq .
varlinkctl introspect /run/systemd/journal/io.systemd.journal io.systemd.Journal
varlinkctl introspect -j /run/systemd/journal/io.systemd.journal io.systemd.Journal | jq .
@ -52,6 +61,7 @@ if [[ -x /usr/lib/systemd/systemd-pcrextend ]]; then
varlinkctl info exec:/usr/lib/systemd/systemd-pcrextend
varlinkctl list-interfaces /usr/lib/systemd/systemd-pcrextend
varlinkctl introspect /usr/lib/systemd/systemd-pcrextend io.systemd.PCRExtend
varlinkctl introspect /usr/lib/systemd/systemd-pcrextend
fi
# SSH transport
@ -83,10 +93,18 @@ SYSTEMD_SSH="$SSHBINDIR/ssh" varlinkctl info ssh:foobar:/run/systemd/journal/io.
# Go through all varlink sockets we can find under /run/systemd/ for some extra coverage
find /run/systemd/ -name "io.systemd*" -type s | while read -r socket; do
varlinkctl info "$socket"
varlinkctl info -j "$socket"
varlinkctl list-interfaces "$socket"
varlinkctl list-interfaces -j "$socket"
varlinkctl list-methods "$socket"
varlinkctl list-methods -j "$socket"
varlinkctl introspect "$socket"
varlinkctl introspect -j "$socket"
varlinkctl list-interfaces "$socket" | while read -r interface; do
varlinkctl introspect "$socket" "$interface"
done
done
(! varlinkctl)
@ -104,9 +122,12 @@ done
(! varlinkctl list-interfaces)
(! varlinkctl list-interfaces "")
(! varlinkctl introspect)
(! varlinkctl introspect /run/systemd/journal/io.systemd.journal)
(! varlinkctl introspect /run/systemd/journal/io.systemd.journal "")
(! varlinkctl introspect "" "")
(! varlinkctl list-methods /run/systemd/journal/io.systemd.journal "")
(! varlinkctl list-methods -j /run/systemd/journal/io.systemd.journal "")
(! varlinkctl list-methods "")
(! varlinkctl list-methods -j "")
(! varlinkctl call)
(! varlinkctl call "")
(! varlinkctl call "" "")