mirror of
https://github.com/systemd/systemd
synced 2024-10-04 15:21:01 +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:
commit
639256f380
|
@ -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>
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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 },
|
||||
|
|
|
@ -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 "" "")
|
||||
|
|
Loading…
Reference in a new issue