mirror of
https://github.com/systemd/systemd.git
synced 2024-12-22 17:35:35 +03:00
busctl: show property values in "introspect" output, add "set-property" command, and support both a terse and a verbose output format
This commit is contained in:
parent
b18ec7e29f
commit
1fc5560911
153
man/busctl.xml
153
man/busctl.xml
@ -161,6 +161,17 @@ along with systemd; If not, see <http://www.gnu.org/licenses/>.
|
||||
</listitem>
|
||||
</varlistentry>
|
||||
|
||||
|
||||
<varlistentry>
|
||||
<term><option>--verbose</option></term>
|
||||
|
||||
<listitem>
|
||||
<para>When used with the <command>call</command> or
|
||||
<command>get-property</command> command shows output in a
|
||||
more verbose format.</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" />
|
||||
@ -238,21 +249,31 @@ along with systemd; If not, see <http://www.gnu.org/licenses/>.
|
||||
<listitem><para>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 arguments,
|
||||
individually formatted as textual parameters.</para></listitem>
|
||||
string is required, followed by the arguments, individually
|
||||
formatted as strings. For details on the formatting used, see
|
||||
below. To suppress output of the returned data use the
|
||||
<option>--quiet</option> option.</para></listitem>
|
||||
</varlistentry>
|
||||
|
||||
<varlistentry>
|
||||
<term><command>get-property</command> <arg choice="plain"><replaceable>SERVICE</replaceable></arg> <arg choice="plain"><replaceable>OBJECT</replaceable></arg> <arg choice="opt"><replaceable>INTERFACE</replaceable> <arg choice="opt" rep="repeat"><replaceable>PROPERTY</replaceable></arg></arg></term>
|
||||
<term><command>get-property</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" rep="repeat"><replaceable>PROPERTY</replaceable></arg></term>
|
||||
|
||||
<listitem><para>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
|
||||
<listitem><para>Retrieve the current value of one or more
|
||||
object properties. Takes a service name, object path,
|
||||
interface name and property name. Multiple properties may be
|
||||
specified at once in which case their values will be shown one
|
||||
after the other.</para></listitem>
|
||||
after the other, separated by newlines. The output is by
|
||||
default in terse format. Use <option>--verbose</option> for a
|
||||
more elaborate output format.</para></listitem>
|
||||
</varlistentry>
|
||||
|
||||
<varlistentry>
|
||||
<term><command>set-property</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>PROPERTY</replaceable></arg> <arg choice="plain"><replaceable>SIGNATURE</replaceable></arg> <arg choice="plain" rep="repeat"><replaceable>ARGUMENT</replaceable></arg></term>
|
||||
|
||||
<listitem><para>Set the current value an object
|
||||
property. Takes a service name, object path, interface name,
|
||||
property name, property signature, followed by a list of
|
||||
parameters formatted as strings.</para></listitem>
|
||||
</varlistentry>
|
||||
|
||||
<varlistentry>
|
||||
@ -263,6 +284,118 @@ along with systemd; If not, see <http://www.gnu.org/licenses/>.
|
||||
</variablelist>
|
||||
</refsect1>
|
||||
|
||||
<refsect1>
|
||||
<title>Parameter Formatting</title>
|
||||
|
||||
<para>The <command>call</command> and
|
||||
<command>set-property</command> commands take a signature
|
||||
string followed by a list of parameters formatted as string
|
||||
(for details on D-Bus signature strings see the <ulink
|
||||
url="http://dbus.freedesktop.org/doc/dbus-specification.html#type-system">Type
|
||||
system chapter of the D-Bus specification</ulink>). For
|
||||
simple types each parameter following the signature should
|
||||
simply be the parameter's value formatted as
|
||||
string. Positive boolean values may be formatted as
|
||||
<literal>true</literal>, <literal>yes</literal>,
|
||||
<literal>on</literal>, <literal>1</literal>; negative
|
||||
boolean values may be specified as <literal>false</literal>,
|
||||
<literal>no</literal>, <literal>off</literal>,
|
||||
<literal>0</literal>. For arrays, a numeric argument for the
|
||||
number of entries followed by the entries shall be
|
||||
specified. For variants the signature of the contents shall
|
||||
be specified, followed by the contents. For dictionaries and
|
||||
structs the contents of them shall be directly
|
||||
specified.</para>
|
||||
|
||||
<para>For example,
|
||||
<programlisting>s jawoll</programlisting> is the formatting
|
||||
of a single string <literal>jawoll</literal>.</para>
|
||||
|
||||
<para>
|
||||
<programlisting>as 3 hello world foobar</programlisting>
|
||||
is the formatting of a string array with three entries,
|
||||
<literal>hello</literal>, <literal>world</literal> and
|
||||
<literal>foobar</literal>.</para>
|
||||
|
||||
<para>
|
||||
<programlisting>a{sv} 3 One s Eins Two u 2 Yes b true</programlisting>
|
||||
is the formatting of a dictionary
|
||||
array that maps strings to variants, consisting of three
|
||||
entries. The string <literal>One</literal> is assigned the
|
||||
string <literal>Eins</literal>. The string
|
||||
<literal>Two</literal> is assigned the 32bit unsigned
|
||||
integer 2. The string <literal>Yes</literal> is assigned a
|
||||
positive boolean.</para>
|
||||
|
||||
<para>Note that the <command>call</command>,
|
||||
<command>get-property</command>,
|
||||
<command>introspect</command> commands will also generate
|
||||
output in this format for the returned data. Since this
|
||||
format is sometimes too terse to be easily understood, the
|
||||
<command>call</command> and <command>get-property</command>
|
||||
commands may generate a more verbose, multi-line output when
|
||||
passed the <option>--verbose</option> option.</para>
|
||||
</refsect1>
|
||||
|
||||
<refsect1>
|
||||
<title>Examples</title>
|
||||
|
||||
<example>
|
||||
<title>Write and Read a Property</title>
|
||||
|
||||
<para>The following two commands first write a
|
||||
property and then read it back. The property is
|
||||
found on the
|
||||
<literal>/org/freedesktop/systemd1</literal> object
|
||||
of the <literal>org.freedesktop.systemd1</literal>
|
||||
service. The name of the property is
|
||||
<literal>LogLevel</literal> on the
|
||||
<literal>org.freedesktop.systemd1.Manager</literal>
|
||||
interface. The property contains a single
|
||||
string:</para>
|
||||
|
||||
<programlisting># busctl set-property org.freedesktop.systemd1 /org/freedesktop/systemd1 org.freedesktop.systemd1.Manager LogLevel s debug
|
||||
# busctl get-property org.freedesktop.systemd1 /org/freedesktop/systemd1 org.freedesktop.systemd1.Manager LogLevel
|
||||
s "debug"</programlisting>
|
||||
|
||||
</example>
|
||||
|
||||
<example>
|
||||
<title>Terse and Verbose Output</title>
|
||||
|
||||
<para>The following two commands read a property that
|
||||
contains an array of strings, and first show it in
|
||||
terse format, followed by verbose format:</para>
|
||||
|
||||
<programlisting>$ busctl get-property org.freedesktop.systemd1 /org/freedesktop/systemd1 org.freedesktop.systemd1.Manager Environment
|
||||
as 2 "LANG=en_US.UTF-8" "PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin"
|
||||
$ busctl get-property --verbose org.freedesktop.systemd1 /org/freedesktop/systemd1 org.freedesktop.systemd1.Manager Environment
|
||||
ARRAY "s" {
|
||||
STRING "LANG=en_US.UTF-8";
|
||||
STRING "PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin";
|
||||
};</programlisting>
|
||||
</example>
|
||||
|
||||
<example>
|
||||
<title>Invoking a Method</title>
|
||||
|
||||
<para>The following command invokes a the
|
||||
<literal>StartUnit</literal> method on the
|
||||
<literal>org.freedesktop.systemd1.Manager</literal>
|
||||
interface of the
|
||||
<literal>/org/freedesktop/systemd1</literal> object
|
||||
of the <literal>org.freedesktop.systemd1</literal>
|
||||
service, and passes it two strings
|
||||
<literal>cups.service</literal> and
|
||||
<literal>replace</literal>. As result of the method
|
||||
call a single object path parameter is received and
|
||||
shown:</para>
|
||||
|
||||
<programlisting># busctl call org.freedesktop.systemd1 /org/freedesktop/systemd1 org.freedesktop.systemd1.Manager StartUnit ss "cups.service" "replace"
|
||||
o "/org/freedesktop/systemd1/job/42684"</programlisting>
|
||||
</example>
|
||||
</refsect1>
|
||||
|
||||
<refsect1>
|
||||
<title>See Also</title>
|
||||
|
||||
|
@ -52,6 +52,7 @@ static bool arg_user = false;
|
||||
static size_t arg_snaplen = 4096;
|
||||
static bool arg_list = false;
|
||||
static bool arg_quiet = false;
|
||||
static bool arg_verbose = false;
|
||||
|
||||
static void pager_open_if_enabled(void) {
|
||||
|
||||
@ -475,12 +476,152 @@ static int tree(sd_bus *bus, char **argv) {
|
||||
return r;
|
||||
}
|
||||
|
||||
static int format_cmdline(sd_bus_message *m, FILE *f, bool needs_space) {
|
||||
int r;
|
||||
|
||||
for (;;) {
|
||||
const char *contents = NULL;
|
||||
char type;
|
||||
union {
|
||||
uint8_t u8;
|
||||
uint16_t u16;
|
||||
int16_t s16;
|
||||
uint32_t u32;
|
||||
int32_t s32;
|
||||
uint64_t u64;
|
||||
int64_t s64;
|
||||
double d64;
|
||||
const char *string;
|
||||
int i;
|
||||
} basic;
|
||||
|
||||
r = sd_bus_message_peek_type(m, &type, &contents);
|
||||
if (r <= 0)
|
||||
return r;
|
||||
|
||||
if (bus_type_is_container(type) > 0) {
|
||||
|
||||
r = sd_bus_message_enter_container(m, type, contents);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
if (type == SD_BUS_TYPE_ARRAY) {
|
||||
unsigned n = 0;
|
||||
|
||||
/* count array entries */
|
||||
for (;;) {
|
||||
|
||||
r = sd_bus_message_skip(m, contents);
|
||||
if (r < 0)
|
||||
return r;
|
||||
if (r == 0)
|
||||
break;
|
||||
|
||||
n++;
|
||||
}
|
||||
|
||||
r = sd_bus_message_rewind(m, false);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
if (needs_space)
|
||||
fputc(' ', f);
|
||||
|
||||
fprintf(f, "%u", n);
|
||||
} else if (type == SD_BUS_TYPE_VARIANT) {
|
||||
|
||||
if (needs_space)
|
||||
fputc(' ', f);
|
||||
|
||||
fprintf(f, "%s", contents);
|
||||
}
|
||||
|
||||
r = format_cmdline(m, f, true);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
r = sd_bus_message_exit_container(m);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
r = sd_bus_message_read_basic(m, type, &basic);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
if (needs_space)
|
||||
fputc(' ', f);
|
||||
|
||||
switch (type) {
|
||||
case SD_BUS_TYPE_BYTE:
|
||||
fprintf(f, "%u", basic.u8);
|
||||
break;
|
||||
|
||||
case SD_BUS_TYPE_BOOLEAN:
|
||||
fputs(true_false(basic.i), f);
|
||||
break;
|
||||
|
||||
case SD_BUS_TYPE_INT16:
|
||||
fprintf(f, "%i", basic.s16);
|
||||
break;
|
||||
|
||||
case SD_BUS_TYPE_UINT16:
|
||||
fprintf(f, "%u", basic.u16);
|
||||
break;
|
||||
|
||||
case SD_BUS_TYPE_INT32:
|
||||
fprintf(f, "%i", basic.s32);
|
||||
break;
|
||||
|
||||
case SD_BUS_TYPE_UINT32:
|
||||
fprintf(f, "%u", basic.u32);
|
||||
break;
|
||||
|
||||
case SD_BUS_TYPE_INT64:
|
||||
fprintf(f, "%" PRIi64, basic.s64);
|
||||
break;
|
||||
|
||||
case SD_BUS_TYPE_UINT64:
|
||||
fprintf(f, "%" PRIu64, basic.u64);
|
||||
break;
|
||||
|
||||
case SD_BUS_TYPE_DOUBLE:
|
||||
fprintf(f, "%g", basic.d64);
|
||||
break;
|
||||
|
||||
case SD_BUS_TYPE_STRING:
|
||||
case SD_BUS_TYPE_OBJECT_PATH:
|
||||
case SD_BUS_TYPE_SIGNATURE: {
|
||||
_cleanup_free_ char *b = NULL;
|
||||
|
||||
b = cescape(basic.string);
|
||||
if (!b)
|
||||
return -ENOMEM;
|
||||
|
||||
fprintf(f, "\"%s\"", b);
|
||||
break;
|
||||
}
|
||||
|
||||
case SD_BUS_TYPE_UNIX_FD:
|
||||
fprintf(f, "%i", basic.i);
|
||||
break;
|
||||
|
||||
default:
|
||||
assert_not_reached("Unknown basic type.");
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
typedef struct Member {
|
||||
const char *type;
|
||||
char *interface;
|
||||
char *name;
|
||||
char *signature;
|
||||
char *result;
|
||||
char *value;
|
||||
bool writable;
|
||||
uint64_t flags;
|
||||
} Member;
|
||||
@ -550,6 +691,7 @@ static void member_free(Member *m) {
|
||||
free(m->name);
|
||||
free(m->signature);
|
||||
free(m->result);
|
||||
free(m->value);
|
||||
free(m);
|
||||
}
|
||||
|
||||
@ -758,16 +900,93 @@ static int introspect(sd_bus *bus, char **argv) {
|
||||
if (r < 0)
|
||||
return bus_log_parse_error(r);
|
||||
|
||||
/* First, get list of all properties */
|
||||
r = parse_xml_introspect(argv[2], xml, &ops, members);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
/* Second, find the current values for them */
|
||||
SET_FOREACH(m, members, i) {
|
||||
|
||||
if (!streq(m->type, "property"))
|
||||
continue;
|
||||
|
||||
if (m->value)
|
||||
continue;
|
||||
|
||||
r = sd_bus_call_method(bus, argv[1], argv[2], "org.freedesktop.DBus.Properties", "GetAll", &error, &reply, "s", m->interface);
|
||||
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 (;;) {
|
||||
Member *z;
|
||||
_cleanup_free_ char *buf = NULL;
|
||||
_cleanup_fclose_ FILE *mf = NULL;
|
||||
size_t sz = 0;
|
||||
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);
|
||||
|
||||
r = sd_bus_message_enter_container(reply, 'v', NULL);
|
||||
if (r < 0)
|
||||
return bus_log_parse_error(r);
|
||||
|
||||
mf = open_memstream(&buf, &sz);
|
||||
if (!mf)
|
||||
return log_oom();
|
||||
|
||||
r = format_cmdline(reply, mf, false);
|
||||
if (r < 0)
|
||||
return bus_log_parse_error(r);
|
||||
|
||||
fclose(mf);
|
||||
mf = NULL;
|
||||
|
||||
z = set_get(members, &((Member) {
|
||||
.type = "property",
|
||||
.interface = m->interface,
|
||||
.name = (char*) name }));
|
||||
if (z) {
|
||||
free(z->value);
|
||||
z->value = buf;
|
||||
buf = NULL;
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
r = sd_bus_message_exit_container(reply);
|
||||
if (r < 0)
|
||||
return bus_log_parse_error(r);
|
||||
}
|
||||
|
||||
pager_open_if_enabled();
|
||||
|
||||
name_width = strlen("NAME");
|
||||
type_width = strlen("TYPE");
|
||||
signature_width = strlen("SIGNATURE");
|
||||
result_width = strlen("RESULT");
|
||||
result_width = strlen("RESULT/VALUE");
|
||||
|
||||
sorted = newa(Member*, set_size(members));
|
||||
|
||||
@ -782,27 +1001,45 @@ static int introspect(sd_bus *bus, char **argv) {
|
||||
signature_width = MAX(signature_width, strlen(m->signature));
|
||||
if (m->result)
|
||||
result_width = MAX(result_width, strlen(m->result));
|
||||
if (m->value)
|
||||
result_width = MAX(result_width, strlen(m->value));
|
||||
|
||||
sorted[k++] = m;
|
||||
}
|
||||
|
||||
if (result_width > 40)
|
||||
result_width = 40;
|
||||
|
||||
assert(k == set_size(members));
|
||||
qsort(sorted, k, sizeof(Member*), member_compare_funcp);
|
||||
|
||||
if (arg_legend) {
|
||||
printf("%-*s %-*s %-*s %-*s %s\n",
|
||||
(int) name_width, "NAME",
|
||||
(int) type_width, "TYPE",
|
||||
(int) signature_width, "SIGNATURE",
|
||||
(int) result_width, "RESULT",
|
||||
(int) result_width, "RESULT/VALUE",
|
||||
"FLAGS");
|
||||
}
|
||||
|
||||
for (j = 0; j < k; j++) {
|
||||
_cleanup_free_ char *ellipsized = NULL;
|
||||
const char *rv;
|
||||
bool is_interface;
|
||||
|
||||
m = sorted[j];
|
||||
|
||||
is_interface = streq(m->type, "interface");
|
||||
|
||||
if (m->value) {
|
||||
ellipsized = ellipsize(m->value, result_width, 100);
|
||||
if (!ellipsized)
|
||||
return log_oom();
|
||||
|
||||
rv = ellipsized;
|
||||
} else
|
||||
rv = strdash(m->result);
|
||||
|
||||
printf("%s%s%-*s%s %-*s %-*s %-*s%s%s%s%s%s%s\n",
|
||||
is_interface ? ansi_highlight() : "",
|
||||
is_interface ? "" : ".",
|
||||
@ -810,7 +1047,7 @@ static int introspect(sd_bus *bus, char **argv) {
|
||||
is_interface ? ansi_highlight_off() : "",
|
||||
(int) type_width, strdash(m->type),
|
||||
(int) signature_width, strdash(m->signature),
|
||||
(int) result_width, strdash(m->result),
|
||||
(int) result_width, rv,
|
||||
(m->flags & SD_BUS_VTABLE_DEPRECATED) ? " deprecated" : (m->flags || m->writable ? "" : " -"),
|
||||
(m->flags & SD_BUS_VTABLE_METHOD_NO_REPLY) ? " no-reply" : "",
|
||||
(m->flags & SD_BUS_VTABLE_PROPERTY_CONST) ? " const" : "",
|
||||
@ -1241,9 +1478,26 @@ static int call(sd_bus *bus, char *argv[]) {
|
||||
r = sd_bus_message_is_empty(reply);
|
||||
if (r < 0)
|
||||
return bus_log_parse_error(r);
|
||||
|
||||
if (r == 0 && !arg_quiet) {
|
||||
|
||||
if (arg_verbose) {
|
||||
pager_open_if_enabled();
|
||||
bus_message_dump(reply, stdout, 0);
|
||||
|
||||
r = bus_message_dump(reply, stdout, 0);
|
||||
if (r < 0)
|
||||
return r;
|
||||
} else {
|
||||
|
||||
fputs(sd_bus_message_get_signature(reply, true), stdout);
|
||||
fputc(' ', stdout);
|
||||
|
||||
r = format_cmdline(reply, stdout, false);
|
||||
if (r < 0)
|
||||
return bus_log_parse_error(r);
|
||||
|
||||
fputc('\n', stdout);
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
@ -1252,75 +1506,21 @@ static int call(sd_bus *bus, char *argv[]) {
|
||||
static int get_property(sd_bus *bus, char *argv[]) {
|
||||
_cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
|
||||
unsigned n;
|
||||
char **i;
|
||||
int r;
|
||||
|
||||
assert(bus);
|
||||
|
||||
n = strv_length(argv);
|
||||
if (n < 3) {
|
||||
log_error("Expects at least three arguments.");
|
||||
if (n < 5) {
|
||||
log_error("Expects at least four 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;
|
||||
const char *contents = NULL;
|
||||
char type;
|
||||
|
||||
r = sd_bus_call_method(bus, argv[1], argv[2], "org.freedesktop.DBus.Properties", "Get", &error, &reply, "ss", argv[3], *i);
|
||||
if (r < 0) {
|
||||
@ -1328,23 +1528,84 @@ static int get_property(sd_bus *bus, char *argv[]) {
|
||||
return r;
|
||||
}
|
||||
|
||||
r = sd_bus_message_enter_container(reply, 'v', NULL);
|
||||
r = sd_bus_message_peek_type(reply, &type, &contents);
|
||||
if (r < 0)
|
||||
return bus_log_parse_error(r);
|
||||
|
||||
if (i > argv + 4)
|
||||
printf("\n");
|
||||
|
||||
if (argv[5])
|
||||
printf("Property %s:\n", *i);
|
||||
r = sd_bus_message_enter_container(reply, 'v', contents);
|
||||
if (r < 0)
|
||||
return bus_log_parse_error(r);
|
||||
|
||||
if (arg_verbose) {
|
||||
pager_open_if_enabled();
|
||||
bus_message_dump(reply, stdout, BUS_MESSAGE_DUMP_SUBTREE_ONLY);
|
||||
|
||||
r = bus_message_dump(reply, stdout, BUS_MESSAGE_DUMP_SUBTREE_ONLY);
|
||||
if (r < 0)
|
||||
return r;
|
||||
} else {
|
||||
fputs(contents, stdout);
|
||||
fputc(' ', stdout);
|
||||
|
||||
r = format_cmdline(reply, stdout, false);
|
||||
if (r < 0)
|
||||
return bus_log_parse_error(r);
|
||||
|
||||
fputc('\n', stdout);
|
||||
}
|
||||
|
||||
r = sd_bus_message_exit_container(reply);
|
||||
if (r < 0)
|
||||
return bus_log_parse_error(r);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int set_property(sd_bus *bus, char *argv[]) {
|
||||
_cleanup_bus_message_unref_ sd_bus_message *m = NULL;
|
||||
_cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
|
||||
unsigned n;
|
||||
char **p;
|
||||
int r;
|
||||
|
||||
assert(bus);
|
||||
|
||||
n = strv_length(argv);
|
||||
if (n < 6) {
|
||||
log_error("Expects at least five arguments.");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
r = sd_bus_message_new_method_call(bus, &m, argv[1], argv[2], "org.freedesktop.DBus.Properties", "Set");
|
||||
if (r < 0)
|
||||
return bus_log_create_error(r);
|
||||
|
||||
r = sd_bus_message_append(m, "ss", argv[3], argv[4]);
|
||||
if (r < 0)
|
||||
return bus_log_create_error(r);
|
||||
|
||||
r = sd_bus_message_open_container(m, 'v', argv[5]);
|
||||
if (r < 0)
|
||||
return bus_log_create_error(r);
|
||||
|
||||
p = argv+6;
|
||||
r = message_append_cmdline(m, argv[5], &p);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
r = sd_bus_message_close_container(m);
|
||||
if (r < 0)
|
||||
return bus_log_create_error(r);
|
||||
|
||||
if (*p) {
|
||||
log_error("Too many parameters for signature.");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
r = sd_bus_call(bus, m, 0, &error, NULL);
|
||||
if (r < 0) {
|
||||
log_error("%s", bus_error_message(&error, r));
|
||||
return r;
|
||||
}
|
||||
|
||||
return 0;
|
||||
@ -1368,18 +1629,21 @@ static int help(void) {
|
||||
" --activatable Only show activatable names\n"
|
||||
" --match=MATCH Only show matching messages\n"
|
||||
" --list Don't show tree, but simple object path list\n"
|
||||
" --quiet Don't show method call reply\n\n"
|
||||
" --quiet Don't show method call reply\n"
|
||||
" --verbose Show result values in long format\n\n"
|
||||
"Commands:\n"
|
||||
" list List bus names\n"
|
||||
" status SERVICE Show service name status\n"
|
||||
" monitor [SERVICE...] Show bus traffic\n"
|
||||
" capture [SERVICE...] Capture bus traffic as pcap\n"
|
||||
" tree [SERVICE...] Show object tree of service\n"
|
||||
" introspect SERVICE PATH\n"
|
||||
" introspect SERVICE OBJECT\n"
|
||||
" call SERVICE OBJECT INTERFACE METHOD [SIGNATURE [ARGUMENT...]]\n"
|
||||
" Call a method\n"
|
||||
" get-property SERVICE OBJECT [INTERFACE [PROPERTY...]]\n"
|
||||
" get-property SERVICE OBJECT INTERFACE PROPERTY...\n"
|
||||
" Get property value\n"
|
||||
" set-property SERVICE OBJECT INTERFACE PROPERTY SIGNATURE ARGUMENT...\n"
|
||||
" Set property value\n"
|
||||
" help Show this help\n"
|
||||
, program_invocation_short_name);
|
||||
|
||||
@ -1402,6 +1666,7 @@ static int parse_argv(int argc, char *argv[]) {
|
||||
ARG_ACTIVATABLE,
|
||||
ARG_SIZE,
|
||||
ARG_LIST,
|
||||
ARG_VERBOSE,
|
||||
};
|
||||
|
||||
static const struct option options[] = {
|
||||
@ -1422,6 +1687,7 @@ static int parse_argv(int argc, char *argv[]) {
|
||||
{ "size", required_argument, NULL, ARG_SIZE },
|
||||
{ "list", no_argument, NULL, ARG_LIST },
|
||||
{ "quiet", no_argument, NULL, 'q' },
|
||||
{ "verbose", no_argument, NULL, ARG_VERBOSE },
|
||||
{},
|
||||
};
|
||||
|
||||
@ -1519,6 +1785,10 @@ static int parse_argv(int argc, char *argv[]) {
|
||||
arg_quiet = true;
|
||||
break;
|
||||
|
||||
case ARG_VERBOSE:
|
||||
arg_verbose = true;
|
||||
break;
|
||||
|
||||
case '?':
|
||||
return -EINVAL;
|
||||
|
||||
@ -1557,6 +1827,9 @@ static int busctl_main(sd_bus *bus, int argc, char *argv[]) {
|
||||
if (streq(argv[optind], "get-property"))
|
||||
return get_property(bus, argv + optind);
|
||||
|
||||
if (streq(argv[optind], "set-property"))
|
||||
return set_property(bus, argv + optind);
|
||||
|
||||
if (streq(argv[optind], "help"))
|
||||
return help();
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user