summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLennart Poettering <lennart@poettering.net>2014-11-14 13:11:10 +0100
committerLennart Poettering <lennart@poettering.net>2014-11-14 13:18:51 +0100
commit781fa93815fafd02b5287ef5781b92ef7b99973b (patch)
tree5ff4471b65649d3b98fe27fd98baab0d8e7830e4
parent8022212b3b8e553fd83b87575f3e730e56852d5d (diff)
busctl: add new "call" command to invoke methods on a service
-rw-r--r--man/busctl.xml15
-rw-r--r--src/libsystemd/sd-bus/busctl.c322
-rw-r--r--src/shared/util.c40
-rw-r--r--src/shared/util.h3
4 files changed, 377 insertions, 3 deletions
diff --git a/man/busctl.xml b/man/busctl.xml
index f9bcea3c2d..783b2a2532 100644
--- a/man/busctl.xml
+++ b/man/busctl.xml
@@ -152,6 +152,15 @@ along with systemd; If not, see <http://www.gnu.org/licenses/>.
</listitem>
</varlistentry>
+ <varlistentry>
+ <term><option>--quiet</option></term>
+
+ <listitem>
+ <para>When used with the <command>call</command> command suppresses
+ display of the response message.</para>
+ </listitem>
+ </varlistentry>
+
<xi:include href="user-system-options.xml" xpointer="user" />
<xi:include href="user-system-options.xml" xpointer="system" />
<xi:include href="user-system-options.xml" xpointer="host" />
@@ -216,6 +225,12 @@ along with systemd; If not, see <http://www.gnu.org/licenses/>.
</varlistentry>
<varlistentry>
+ <term><command>call</command> <arg choice="plain"><replaceable>SERVICE</replaceable></arg> <arg choice="plain"><replaceable>OBJECT</replaceable></arg> <arg choice="plain"><replaceable>INTERFACE</replaceable></arg> <arg choice="plain"><replaceable>METHOD</replaceable></arg> <arg choice="opt"><replaceable>SIGNATURE</replaceable> <arg choice="opt" rep="repeat"><replaceable>PARAMETERS</replaceable></arg></arg></term>
+
+ <listitem><para>Invoke a method and show the response.</para></listitem>
+ </varlistentry>
+
+ <varlistentry>
<term><command>help</command></term>
<listitem><para>Show command syntax help.</para></listitem>
diff --git a/src/libsystemd/sd-bus/busctl.c b/src/libsystemd/sd-bus/busctl.c
index b241466e25..ea8425ac23 100644
--- a/src/libsystemd/sd-bus/busctl.c
+++ b/src/libsystemd/sd-bus/busctl.c
@@ -34,6 +34,7 @@
#include "bus-internal.h"
#include "bus-util.h"
#include "bus-dump.h"
+#include "bus-signature.h"
static bool arg_no_pager = false;
static bool arg_legend = true;
@@ -48,6 +49,7 @@ static char *arg_host = NULL;
static bool arg_user = false;
static size_t arg_snaplen = 4096;
static bool arg_list = false;
+static bool arg_quiet = false;
static void pager_open_if_enabled(void) {
@@ -1139,6 +1141,309 @@ static int status(sd_bus *bus, char *argv[]) {
return 0;
}
+static int message_append_cmdline(sd_bus_message *m, const char *signature, char ***x) {
+ char **p;
+ int r;
+
+ assert(m);
+ assert(signature);
+ assert(x);
+
+ p = *x;
+
+ for (;;) {
+ const char *v;
+ char t;
+
+ t = *signature;
+ v = *p;
+
+ if (t == 0)
+ break;
+ if (!v) {
+ log_error("Too few parameters for signature.");
+ return -EINVAL;
+ }
+
+ signature++;
+ p++;
+
+ switch (t) {
+
+ case SD_BUS_TYPE_BOOLEAN:
+
+ r = parse_boolean(v);
+ if (r < 0) {
+ log_error("Failed to parse as boolean: %s", v);
+ return r;
+ }
+
+ r = sd_bus_message_append_basic(m, t, &r);
+ break;
+
+ case SD_BUS_TYPE_BYTE: {
+ uint8_t z;
+
+ r = safe_atou8(v, &z);
+ if (r < 0) {
+ log_error("Failed to parse as byte (unsigned 8bit integer): %s", v);
+ return r;
+ }
+
+ r = sd_bus_message_append_basic(m, t, &z);
+ break;
+ }
+
+ case SD_BUS_TYPE_INT16: {
+ int16_t z;
+
+ r = safe_atoi16(v, &z);
+ if (r < 0) {
+ log_error("Failed to parse as signed 16bit integer: %s", v);
+ return r;
+ }
+
+ r = sd_bus_message_append_basic(m, t, &z);
+ break;
+ }
+
+ case SD_BUS_TYPE_UINT16: {
+ uint16_t z;
+
+ r = safe_atou16(v, &z);
+ if (r < 0) {
+ log_error("Failed to parse as unsigned 16bit integer: %s", v);
+ return r;
+ }
+
+ r = sd_bus_message_append_basic(m, t, &z);
+ break;
+ }
+
+ case SD_BUS_TYPE_INT32: {
+ int32_t z;
+
+ r = safe_atoi32(v, &z);
+ if (r < 0) {
+ log_error("Failed to parse as signed 32bit integer: %s", v);
+ return r;
+ }
+
+ r = sd_bus_message_append_basic(m, t, &z);
+ break;
+ }
+
+ case SD_BUS_TYPE_UINT32: {
+ uint32_t z;
+
+ r = safe_atou32(v, &z);
+ if (r < 0) {
+ log_error("Failed to parse as unsigned 32bit integer: %s", v);
+ return r;
+ }
+
+ r = sd_bus_message_append_basic(m, t, &z);
+ break;
+ }
+
+ case SD_BUS_TYPE_INT64: {
+ int64_t z;
+
+ r = safe_atoi64(v, &z);
+ if (r < 0) {
+ log_error("Failed to parse as signed 64bit integer: %s", v);
+ return r;
+ }
+
+ r = sd_bus_message_append_basic(m, t, &z);
+ break;
+ }
+
+ case SD_BUS_TYPE_UINT64: {
+ uint64_t z;
+
+ r = safe_atou64(v, &z);
+ if (r < 0) {
+ log_error("Failed to parse as unsigned 64bit integer: %s", v);
+ return r;
+ }
+
+ r = sd_bus_message_append_basic(m, t, &z);
+ break;
+ }
+
+
+ case SD_BUS_TYPE_DOUBLE: {
+ double z;
+
+ r = safe_atod(v, &z);
+ if (r < 0) {
+ log_error("Failed to parse as double precision floating point: %s", v);
+ return r;
+ }
+
+ r = sd_bus_message_append_basic(m, t, &z);
+ break;
+ }
+
+ case SD_BUS_TYPE_STRING:
+ case SD_BUS_TYPE_OBJECT_PATH:
+ case SD_BUS_TYPE_SIGNATURE:
+
+ r = sd_bus_message_append_basic(m, t, v);
+ break;
+
+ case SD_BUS_TYPE_ARRAY: {
+ uint32_t n;
+ size_t k;
+
+ r = safe_atou32(v, &n);
+ if (r < 0) {
+ log_error("Failed to parse number of array entries: %s", v);
+ return r;
+ }
+
+ r = signature_element_length(signature, &k);
+ if (r < 0) {
+ log_error("Invalid array signature.");
+ return r;
+ }
+
+ {
+ unsigned i;
+ char s[k + 1];
+ memcpy(s, signature, k);
+ s[k] = 0;
+
+ r = sd_bus_message_open_container(m, SD_BUS_TYPE_ARRAY, s);
+ if (r < 0)
+ return bus_log_create_error(r);
+
+ for (i = 0; i < n; i++) {
+ r = message_append_cmdline(m, s, &p);
+ if (r < 0)
+ return r;
+ }
+ }
+
+ signature += k;
+
+ r = sd_bus_message_close_container(m);
+ break;
+ }
+
+ case SD_BUS_TYPE_VARIANT:
+ r = sd_bus_message_open_container(m, SD_BUS_TYPE_VARIANT, v);
+ if (r < 0)
+ return bus_log_create_error(r);
+
+ r = message_append_cmdline(m, v, &p);
+ if (r < 0)
+ return r;
+
+ r = sd_bus_message_close_container(m);
+ break;
+
+ case SD_BUS_TYPE_STRUCT_BEGIN:
+ case SD_BUS_TYPE_DICT_ENTRY_BEGIN: {
+ size_t k;
+
+ signature--;
+ p--;
+
+ r = signature_element_length(signature, &k);
+ if (r < 0) {
+ log_error("Invalid struct/dict entry signature.");
+ return r;
+ }
+
+ {
+ char s[k-1];
+ memcpy(s, signature + 1, k - 2);
+ s[k - 2] = 0;
+
+ r = sd_bus_message_open_container(m, t == SD_BUS_TYPE_STRUCT_BEGIN ? SD_BUS_TYPE_STRUCT : SD_BUS_TYPE_DICT_ENTRY, s);
+ if (r < 0)
+ return bus_log_create_error(r);
+
+ r = message_append_cmdline(m, s, &p);
+ if (r < 0)
+ return r;
+ }
+
+ signature += k;
+
+ r = sd_bus_message_close_container(m);
+ break;
+ }
+
+ case SD_BUS_TYPE_UNIX_FD:
+ log_error("UNIX file descriptor not supported as type.");
+ return -EINVAL;
+
+ default:
+ log_error("Unknown signature type %c.", t);
+ return -EINVAL;
+ }
+
+ if (r < 0)
+ return bus_log_create_error(r);
+ }
+
+ *x = p;
+ return 0;
+}
+
+static int call(sd_bus *bus, char *argv[]) {
+ _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
+ _cleanup_bus_message_unref_ sd_bus_message *m = NULL, *reply = NULL;
+ int r;
+
+ assert(bus);
+
+ if (strv_length(argv) < 5) {
+ log_error("Expects at least four arguments.");
+ return -EINVAL;
+ }
+
+ r = sd_bus_message_new_method_call(bus, &m, argv[1], argv[2], argv[3], argv[4]);
+ if (r < 0) {
+ log_error("Failed to prepare bus message: %s", strerror(-r));
+ return r;
+ }
+
+ if (!isempty(argv[5])) {
+ char **p;
+
+ p = argv+6;
+
+ r = message_append_cmdline(m, argv[5], &p);
+ if (r < 0)
+ return r;
+
+ if (*p) {
+ log_error("Too many parameters for signature.");
+ return -EINVAL;
+ }
+ }
+
+ r = sd_bus_call(bus, m, 0, &error, &reply);
+ if (r < 0) {
+ log_error("%s", bus_error_message(&error, r));
+ return r;
+ }
+
+ r = sd_bus_message_is_empty(reply);
+ if (r < 0)
+ return bus_log_parse_error(r);
+ if (r == 0 && !arg_quiet) {
+ pager_open_if_enabled();
+ bus_message_dump(reply, stdout, false);
+ }
+
+ return 0;
+}
+
static int help(void) {
printf("%s [OPTIONS...] {COMMAND} ...\n\n"
"Introspect the bus.\n\n"
@@ -1156,13 +1461,16 @@ static int help(void) {
" --acquired Only show acquired names\n"
" --activatable Only show activatable names\n"
" --match=MATCH Only show matching messages\n"
- " --list Don't show tree, but simple object path list\n\n"
+ " --list Don't show tree, but simple object path list\n"
+ " --quiet Don't show method call reply\n\n"
"Commands:\n"
" list List bus names\n"
" tree [SERVICE...] Show object tree of service\n"
" monitor [SERVICE...] Show bus traffic\n"
" capture [SERVICE...] Capture bus traffic as pcap\n"
- " status NAME Show name status\n"
+ " status SERVICE Show service name status\n"
+ " call SERVICE PATH INTERFACE METHOD [SIGNATURE] [ARGUMENTS...]\n"
+ " Call a method\n"
" help Show this help\n"
, program_invocation_short_name);
@@ -1204,6 +1512,7 @@ static int parse_argv(int argc, char *argv[]) {
{ "machine", required_argument, NULL, 'M' },
{ "size", required_argument, NULL, ARG_SIZE },
{ "list", no_argument, NULL, ARG_LIST },
+ { "quiet", no_argument, NULL, 'q' },
{},
};
@@ -1212,7 +1521,7 @@ static int parse_argv(int argc, char *argv[]) {
assert(argc >= 0);
assert(argv);
- while ((c = getopt_long(argc, argv, "hH:M:", options, NULL)) >= 0)
+ while ((c = getopt_long(argc, argv, "hH:M:q", options, NULL)) >= 0)
switch (c) {
@@ -1297,6 +1606,10 @@ static int parse_argv(int argc, char *argv[]) {
arg_host = optarg;
break;
+ case 'q':
+ arg_quiet = true;
+ break;
+
case '?':
return -EINVAL;
@@ -1326,6 +1639,9 @@ static int busctl_main(sd_bus *bus, int argc, char *argv[]) {
if (streq(argv[optind], "tree"))
return tree(bus, argv + optind);
+ if (streq(argv[optind], "call"))
+ return call(bus, argv + optind);
+
if (streq(argv[optind], "help"))
return help();
diff --git a/src/shared/util.c b/src/shared/util.c
index 2f4fa237dd..eeced4769a 100644
--- a/src/shared/util.c
+++ b/src/shared/util.c
@@ -363,6 +363,46 @@ int safe_atou8(const char *s, uint8_t *ret) {
return 0;
}
+int safe_atou16(const char *s, uint16_t *ret) {
+ char *x = NULL;
+ unsigned long l;
+
+ assert(s);
+ assert(ret);
+
+ errno = 0;
+ l = strtoul(s, &x, 0);
+
+ if (!x || x == s || *x || errno)
+ return errno > 0 ? -errno : -EINVAL;
+
+ if ((unsigned long) (uint16_t) l != l)
+ return -ERANGE;
+
+ *ret = (uint16_t) l;
+ return 0;
+}
+
+int safe_atoi16(const char *s, int16_t *ret) {
+ char *x = NULL;
+ long l;
+
+ assert(s);
+ assert(ret);
+
+ errno = 0;
+ l = strtol(s, &x, 0);
+
+ if (!x || x == s || *x || errno)
+ return errno > 0 ? -errno : -EINVAL;
+
+ if ((long) (int16_t) l != l)
+ return -ERANGE;
+
+ *ret = (int16_t) l;
+ return 0;
+}
+
int safe_atollu(const char *s, long long unsigned *ret_llu) {
char *x = NULL;
unsigned long long l;
diff --git a/src/shared/util.h b/src/shared/util.h
index 04f2d8a564..835fee496d 100644
--- a/src/shared/util.h
+++ b/src/shared/util.h
@@ -245,6 +245,9 @@ static inline int safe_atoi64(const char *s, int64_t *ret_i) {
return safe_atolli(s, (long long int*) ret_i);
}
+int safe_atou16(const char *s, uint16_t *ret);
+int safe_atoi16(const char *s, int16_t *ret);
+
const char* split(const char **state, size_t *l, const char *separator, bool quoted);
#define FOREACH_WORD(word, length, s, state) \