diff --git a/man/busctl.xml b/man/busctl.xml
index 6f940c3eb64..730a3344d6c 100644
--- a/man/busctl.xml
+++ b/man/busctl.xml
@@ -227,7 +227,24 @@ along with systemd; If not, see .
call SERVICE OBJECT INTERFACE METHOD SIGNATURE PARAMETERS
- Invoke a method and show the response.
+ Invoke a method and show the response. Takes a
+ service name, object path, interface name and method name. If
+ parameters shall be passed to the method call a signature
+ string is required, followed by the individual parameters,
+ individually formatted as textual arguments.
+
+
+
+ get-property SERVICE OBJECT INTERFACE PROPERTIES
+
+ Retrieve the current value one or more object
+ properties. Takes a service name and object path. Optionally
+ takes an interface name and property name. If the property
+ name is omited, shows all properties on the selected
+ interface. If the interface is also omitted shows the
+ properties of all interfaces. Multiple properties may be
+ specified at once in which case their values will be shown one
+ after the other.
diff --git a/src/libsystemd/sd-bus/bus-dump.c b/src/libsystemd/sd-bus/bus-dump.c
index b13bc4b119b..28fcdda77bf 100644
--- a/src/libsystemd/sd-bus/bus-dump.c
+++ b/src/libsystemd/sd-bus/bus-dump.c
@@ -30,21 +30,34 @@
#include "bus-type.h"
#include "bus-dump.h"
-static char *indent(unsigned level) {
+static char *indent(unsigned level, unsigned flags) {
char *p;
+ unsigned n, i = 0;
- p = new(char, 2 + level + 1);
+ n = 0;
+
+ if (flags & BUS_MESSAGE_DUMP_SUBTREE_ONLY && level > 0)
+ level -= 1;
+
+ if (flags & BUS_MESSAGE_DUMP_WITH_HEADER)
+ n += 2;
+
+ p = new(char, n + level*8 + 1);
if (!p)
return NULL;
- p[0] = p[1] = ' ';
- memset(p + 2, '\t', level);
- p[2 + level] = 0;
+ if (flags & BUS_MESSAGE_DUMP_WITH_HEADER) {
+ p[i++] = ' ';
+ p[i++] = ' ';
+ }
+
+ memset(p + i, ' ', level*8);
+ p[i + level*8] = 0;
return p;
}
-int bus_message_dump(sd_bus_message *m, FILE *f, bool with_header) {
+int bus_message_dump(sd_bus_message *m, FILE *f, unsigned flags) {
unsigned level = 1;
int r;
@@ -53,7 +66,7 @@ int bus_message_dump(sd_bus_message *m, FILE *f, bool with_header) {
if (!f)
f = stdout;
- if (with_header) {
+ if (flags & BUS_MESSAGE_DUMP_WITH_HEADER) {
fprintf(f,
"%s%s%s Type=%s%s%s Endian=%c Flags=%u Version=%u Priority=%lli",
m->header->type == SD_BUS_MESSAGE_METHOD_ERROR ? ansi_highlight_red() :
@@ -111,13 +124,14 @@ int bus_message_dump(sd_bus_message *m, FILE *f, bool with_header) {
bus_creds_dump(&m->creds, f);
}
- r = sd_bus_message_rewind(m, true);
+ r = sd_bus_message_rewind(m, !(flags & BUS_MESSAGE_DUMP_SUBTREE_ONLY));
if (r < 0) {
log_error("Failed to rewind: %s", strerror(-r));
return r;
}
- fprintf(f, " MESSAGE \"%s\" {\n", strempty(m->root_container.signature));
+ if (!(flags & BUS_MESSAGE_DUMP_SUBTREE_ONLY))
+ fprintf(f, "%sMESSAGE \"%s\" {\n", indent(0, flags), strempty(m->root_container.signature));
for (;;) {
_cleanup_free_ char *prefix = NULL;
@@ -154,7 +168,7 @@ int bus_message_dump(sd_bus_message *m, FILE *f, bool with_header) {
level--;
- prefix = indent(level);
+ prefix = indent(level, flags);
if (!prefix)
return log_oom();
@@ -162,7 +176,7 @@ int bus_message_dump(sd_bus_message *m, FILE *f, bool with_header) {
continue;
}
- prefix = indent(level);
+ prefix = indent(level, flags);
if (!prefix)
return log_oom();
@@ -254,7 +268,9 @@ int bus_message_dump(sd_bus_message *m, FILE *f, bool with_header) {
}
}
- fprintf(f, " };\n\n");
+ if (!(flags & BUS_MESSAGE_DUMP_SUBTREE_ONLY))
+ fprintf(f, "%s};\n\n", indent(0, flags));
+
return 0;
}
diff --git a/src/libsystemd/sd-bus/bus-dump.h b/src/libsystemd/sd-bus/bus-dump.h
index 6af0ab066cb..360c844cf96 100644
--- a/src/libsystemd/sd-bus/bus-dump.h
+++ b/src/libsystemd/sd-bus/bus-dump.h
@@ -26,7 +26,12 @@
#include "sd-bus.h"
-int bus_message_dump(sd_bus_message *m, FILE *f, bool with_header);
+enum {
+ BUS_MESSAGE_DUMP_WITH_HEADER = 1,
+ BUS_MESSAGE_DUMP_SUBTREE_ONLY = 2,
+};
+
+int bus_message_dump(sd_bus_message *m, FILE *f, unsigned flags);
int bus_creds_dump(sd_bus_creds *c, FILE *f);
diff --git a/src/libsystemd/sd-bus/busctl.c b/src/libsystemd/sd-bus/busctl.c
index 2bf890e88fa..6d03692c7f0 100644
--- a/src/libsystemd/sd-bus/busctl.c
+++ b/src/libsystemd/sd-bus/busctl.c
@@ -1023,7 +1023,7 @@ static int tree(sd_bus *bus, char **argv) {
}
static int message_dump(sd_bus_message *m, FILE *f) {
- return bus_message_dump(m, f, true);
+ return bus_message_dump(m, f, BUS_MESSAGE_DUMP_WITH_HEADER);
}
static int message_pcap(sd_bus_message *m, FILE *f) {
@@ -1445,7 +1445,108 @@ static int call(sd_bus *bus, char *argv[]) {
return bus_log_parse_error(r);
if (r == 0 && !arg_quiet) {
pager_open_if_enabled();
- bus_message_dump(reply, stdout, false);
+ bus_message_dump(reply, stdout, 0);
+ }
+
+ return 0;
+}
+
+static int get_property(sd_bus *bus, char *argv[]) {
+ _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
+ unsigned n;
+ int r;
+
+ assert(bus);
+
+ n = strv_length(argv);
+ if (n < 3) {
+ log_error("Expects at least three arguments.");
+ return -EINVAL;
+ }
+
+ if (n < 5) {
+ _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
+ bool not_first = false;
+
+ r = sd_bus_call_method(bus, argv[1], argv[2], "org.freedesktop.DBus.Properties", "GetAll", &error, &reply, "s", strempty(argv[3]));
+ if (r < 0) {
+ log_error("%s", bus_error_message(&error, r));
+ return r;
+ }
+
+ r = sd_bus_message_enter_container(reply, 'a', "{sv}");
+ if (r < 0)
+ return bus_log_parse_error(r);
+
+ for (;;) {
+ const char *name;
+
+ r = sd_bus_message_enter_container(reply, 'e', "sv");
+ if (r < 0)
+ return bus_log_parse_error(r);
+
+ if (r == 0)
+ break;
+
+ r = sd_bus_message_read(reply, "s", &name);
+ if (r < 0)
+ return bus_log_parse_error(r);
+
+ if (not_first)
+ printf("\n");
+
+ printf("Property %s:\n", name);
+
+ r = sd_bus_message_enter_container(reply, 'v', NULL);
+ if (r < 0)
+ return bus_log_parse_error(r);
+
+ pager_open_if_enabled();
+ bus_message_dump(reply, stdout, BUS_MESSAGE_DUMP_SUBTREE_ONLY);
+
+ r = sd_bus_message_exit_container(reply);
+ if (r < 0)
+ return bus_log_parse_error(r);
+
+ r = sd_bus_message_exit_container(reply);
+ if (r < 0)
+ return bus_log_parse_error(r);
+
+ not_first = true;
+ }
+
+ r = sd_bus_message_exit_container(reply);
+ if (r < 0)
+ return bus_log_parse_error(r);
+ } else {
+ char **i;
+
+ STRV_FOREACH(i, argv + 4) {
+ _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
+
+ r = sd_bus_call_method(bus, argv[1], argv[2], "org.freedesktop.DBus.Properties", "Get", &error, &reply, "ss", argv[3], *i);
+ if (r < 0) {
+ log_error("%s", bus_error_message(&error, r));
+ return r;
+ }
+
+ r = sd_bus_message_enter_container(reply, 'v', NULL);
+ if (r < 0)
+ return bus_log_parse_error(r);
+
+ if (i > argv + 4)
+ printf("\n");
+
+ if (argv[5])
+ printf("Property %s:\n", *i);
+
+ pager_open_if_enabled();
+ bus_message_dump(reply, stdout, BUS_MESSAGE_DUMP_SUBTREE_ONLY);
+
+ r = sd_bus_message_exit_container(reply);
+ if (r < 0)
+ return bus_log_parse_error(r);
+ }
}
return 0;
@@ -1476,8 +1577,10 @@ static int help(void) {
" monitor [SERVICE...] Show bus traffic\n"
" capture [SERVICE...] Capture bus traffic as pcap\n"
" status SERVICE Show service name status\n"
- " call SERVICE PATH INTERFACE METHOD [SIGNATURE] [ARGUMENTS...]\n"
+ " call SERVICE PATH INTERFACE METHOD [SIGNATURE [ARGUMENTS...]]\n"
" Call a method\n"
+ " get-property SERVICE PATH [INTERFACE [PROPERTY...]]\n"
+ " Get property value\n"
" help Show this help\n"
, program_invocation_short_name);
@@ -1649,6 +1752,9 @@ static int busctl_main(sd_bus *bus, int argc, char *argv[]) {
if (streq(argv[optind], "call"))
return call(bus, argv + optind);
+ if (streq(argv[optind], "get-property"))
+ return get_property(bus, argv + optind);
+
if (streq(argv[optind], "help"))
return help();
diff --git a/src/libsystemd/sd-bus/test-bus-gvariant.c b/src/libsystemd/sd-bus/test-bus-gvariant.c
index 92268588250..56df5d0b48e 100644
--- a/src/libsystemd/sd-bus/test-bus-gvariant.c
+++ b/src/libsystemd/sd-bus/test-bus-gvariant.c
@@ -175,14 +175,14 @@ static void test_marshal(void) {
}
#endif
- assert_se(bus_message_dump(m, NULL, true) >= 0);
+ assert_se(bus_message_dump(m, NULL, BUS_MESSAGE_DUMP_WITH_HEADER) >= 0);
assert_se(bus_message_get_blob(m, &blob, &sz) >= 0);
assert_se(bus_message_from_malloc(bus, blob, sz, NULL, 0, NULL, NULL, &n) >= 0);
blob = NULL;
- assert_se(bus_message_dump(n, NULL, true) >= 0);
+ assert_se(bus_message_dump(n, NULL, BUS_MESSAGE_DUMP_WITH_HEADER) >= 0);
m = sd_bus_message_unref(m);
@@ -191,7 +191,7 @@ static void test_marshal(void) {
assert_se(sd_bus_message_append(m, "as", 0) >= 0);
assert_se(bus_message_seal(m, 4712, 0) >= 0);
- assert_se(bus_message_dump(m, NULL, true) >= 0);
+ assert_se(bus_message_dump(m, NULL, BUS_MESSAGE_DUMP_WITH_HEADER) >= 0);
}
int main(int argc, char *argv[]) {
diff --git a/src/libsystemd/sd-bus/test-bus-kernel.c b/src/libsystemd/sd-bus/test-bus-kernel.c
index b138d390d9e..9cc0f01c5b5 100644
--- a/src/libsystemd/sd-bus/test-bus-kernel.c
+++ b/src/libsystemd/sd-bus/test-bus-kernel.c
@@ -117,7 +117,7 @@ int main(int argc, char *argv[]) {
assert_se(r > 0);
assert_se(m);
- bus_message_dump(m, stdout, true);
+ bus_message_dump(m, stdout, BUS_MESSAGE_DUMP_WITH_HEADER);
assert_se(sd_bus_message_rewind(m, true) >= 0);
r = sd_bus_message_read(m, "s", &the_string);
@@ -154,7 +154,7 @@ int main(int argc, char *argv[]) {
assert_se(r > 0);
assert_se(m);
- bus_message_dump(m, stdout, true);
+ bus_message_dump(m, stdout, BUS_MESSAGE_DUMP_WITH_HEADER);
assert_se(sd_bus_message_rewind(m, true) >= 0);
if (sd_bus_message_is_method_call(m, "an.inter.face", "AMethod")) {
diff --git a/src/libsystemd/sd-bus/test-bus-marshal.c b/src/libsystemd/sd-bus/test-bus-marshal.c
index 95321125247..8cefc7a154e 100644
--- a/src/libsystemd/sd-bus/test-bus-marshal.c
+++ b/src/libsystemd/sd-bus/test-bus-marshal.c
@@ -148,10 +148,10 @@ int main(int argc, char *argv[]) {
r = bus_message_seal(m, 4711, 0);
assert_se(r >= 0);
- bus_message_dump(m, stdout, true);
+ bus_message_dump(m, stdout, BUS_MESSAGE_DUMP_WITH_HEADER);
ms = open_memstream(&first, &first_size);
- bus_message_dump(m, ms, false);
+ bus_message_dump(m, ms, 0);
fflush(ms);
assert_se(!ferror(ms));
@@ -201,11 +201,11 @@ int main(int argc, char *argv[]) {
r = bus_message_from_malloc(bus, buffer, sz, NULL, 0, NULL, NULL, &m);
assert_se(r >= 0);
- bus_message_dump(m, stdout, true);
+ bus_message_dump(m, stdout, BUS_MESSAGE_DUMP_WITH_HEADER);
fclose(ms);
ms = open_memstream(&second, &second_size);
- bus_message_dump(m, ms, false);
+ bus_message_dump(m, ms, 0);
fflush(ms);
assert_se(!ferror(ms));
assert_se(first_size == second_size);
@@ -285,7 +285,7 @@ int main(int argc, char *argv[]) {
fclose(ms);
ms = open_memstream(&third, &third_size);
- bus_message_dump(copy, ms, false);
+ bus_message_dump(copy, ms, 0);
fflush(ms);
assert_se(!ferror(ms));
diff --git a/src/libsystemd/sd-bus/test-bus-objects.c b/src/libsystemd/sd-bus/test-bus-objects.c
index e7a445f3cbb..1324e912fcd 100644
--- a/src/libsystemd/sd-bus/test-bus-objects.c
+++ b/src/libsystemd/sd-bus/test-bus-objects.c
@@ -385,7 +385,7 @@ static int client(struct context *c) {
r = sd_bus_call_method(bus, "org.freedesktop.systemd.test", "/foo", "org.freedesktop.DBus.Properties", "GetAll", &error, &reply, "s", "");
assert_se(r >= 0);
- bus_message_dump(reply, stdout, true);
+ bus_message_dump(reply, stdout, BUS_MESSAGE_DUMP_WITH_HEADER);
sd_bus_message_unref(reply);
reply = NULL;
@@ -403,7 +403,7 @@ static int client(struct context *c) {
r = sd_bus_call_method(bus, "org.freedesktop.systemd.test", "/value", "org.freedesktop.DBus.ObjectManager", "GetManagedObjects", &error, &reply, "");
assert_se(r >= 0);
- bus_message_dump(reply, stdout, true);
+ bus_message_dump(reply, stdout, BUS_MESSAGE_DUMP_WITH_HEADER);
sd_bus_message_unref(reply);
reply = NULL;
@@ -415,7 +415,7 @@ static int client(struct context *c) {
assert_se(r > 0);
assert_se(sd_bus_message_is_signal(reply, "org.freedesktop.DBus.Properties", "PropertiesChanged"));
- bus_message_dump(reply, stdout, true);
+ bus_message_dump(reply, stdout, BUS_MESSAGE_DUMP_WITH_HEADER);
sd_bus_message_unref(reply);
reply = NULL;
@@ -427,7 +427,7 @@ static int client(struct context *c) {
assert_se(r > 0);
assert_se(sd_bus_message_is_signal(reply, "org.freedesktop.DBus.Properties", "PropertiesChanged"));
- bus_message_dump(reply, stdout, true);
+ bus_message_dump(reply, stdout, BUS_MESSAGE_DUMP_WITH_HEADER);
sd_bus_message_unref(reply);
reply = NULL;
@@ -439,7 +439,7 @@ static int client(struct context *c) {
assert_se(r > 0);
assert_se(sd_bus_message_is_signal(reply, "org.freedesktop.DBus.ObjectManager", "InterfacesAdded"));
- bus_message_dump(reply, stdout, true);
+ bus_message_dump(reply, stdout, BUS_MESSAGE_DUMP_WITH_HEADER);
sd_bus_message_unref(reply);
reply = NULL;
@@ -451,7 +451,7 @@ static int client(struct context *c) {
assert_se(r > 0);
assert_se(sd_bus_message_is_signal(reply, "org.freedesktop.DBus.ObjectManager", "InterfacesRemoved"));
- bus_message_dump(reply, stdout, true);
+ bus_message_dump(reply, stdout, BUS_MESSAGE_DUMP_WITH_HEADER);
sd_bus_message_unref(reply);
reply = NULL;
diff --git a/src/libsystemd/sd-bus/test-bus-zero-copy.c b/src/libsystemd/sd-bus/test-bus-zero-copy.c
index e938a48b639..2cc671b16d1 100644
--- a/src/libsystemd/sd-bus/test-bus-zero-copy.c
+++ b/src/libsystemd/sd-bus/test-bus-zero-copy.c
@@ -138,7 +138,7 @@ int main(int argc, char *argv[]) {
r = bus_message_seal(m, 55, 99*USEC_PER_SEC);
assert_se(r >= 0);
- bus_message_dump(m, stdout, true);
+ bus_message_dump(m, stdout, BUS_MESSAGE_DUMP_WITH_HEADER);
r = sd_bus_send(b, m, NULL);
assert_se(r >= 0);
@@ -148,7 +148,7 @@ int main(int argc, char *argv[]) {
r = sd_bus_process(a, &m);
assert_se(r > 0);
- bus_message_dump(m, stdout, true);
+ bus_message_dump(m, stdout, BUS_MESSAGE_DUMP_WITH_HEADER);
sd_bus_message_rewind(m, true);
r = sd_bus_message_enter_container(m, 'r', "aysay");