mirror of
https://github.com/systemd/systemd
synced 2024-10-15 12:34:37 +00: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>
|
</listitem>
|
||||||
</varlistentry>
|
</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="user" />
|
||||||
<xi:include href="user-system-options.xml" xpointer="system" />
|
<xi:include href="user-system-options.xml" xpointer="system" />
|
||||||
<xi:include href="user-system-options.xml" xpointer="host" />
|
<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
|
<listitem><para>Invoke a method and show the response. Takes a
|
||||||
service name, object path, interface name and method name. If
|
service name, object path, interface name and method name. If
|
||||||
parameters shall be passed to the method call a signature
|
parameters shall be passed to the method call a signature
|
||||||
string is required, followed by the individual arguments,
|
string is required, followed by the arguments, individually
|
||||||
individually formatted as textual parameters.</para></listitem>
|
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>
|
||||||
|
|
||||||
<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
|
<listitem><para>Retrieve the current value of one or more
|
||||||
properties. Takes a service name and object path. Optionally
|
object properties. Takes a service name, object path,
|
||||||
takes an interface name and property name. If the property
|
interface name and property name. Multiple properties may be
|
||||||
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
|
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>
|
||||||
|
|
||||||
<varlistentry>
|
<varlistentry>
|
||||||
|
@ -263,6 +284,118 @@ along with systemd; If not, see <http://www.gnu.org/licenses/>.
|
||||||
</variablelist>
|
</variablelist>
|
||||||
</refsect1>
|
</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>
|
<refsect1>
|
||||||
<title>See Also</title>
|
<title>See Also</title>
|
||||||
|
|
||||||
|
|
|
@ -52,6 +52,7 @@ static bool arg_user = false;
|
||||||
static size_t arg_snaplen = 4096;
|
static size_t arg_snaplen = 4096;
|
||||||
static bool arg_list = false;
|
static bool arg_list = false;
|
||||||
static bool arg_quiet = false;
|
static bool arg_quiet = false;
|
||||||
|
static bool arg_verbose = false;
|
||||||
|
|
||||||
static void pager_open_if_enabled(void) {
|
static void pager_open_if_enabled(void) {
|
||||||
|
|
||||||
|
@ -475,12 +476,152 @@ static int tree(sd_bus *bus, char **argv) {
|
||||||
return r;
|
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 {
|
typedef struct Member {
|
||||||
const char *type;
|
const char *type;
|
||||||
char *interface;
|
char *interface;
|
||||||
char *name;
|
char *name;
|
||||||
char *signature;
|
char *signature;
|
||||||
char *result;
|
char *result;
|
||||||
|
char *value;
|
||||||
bool writable;
|
bool writable;
|
||||||
uint64_t flags;
|
uint64_t flags;
|
||||||
} Member;
|
} Member;
|
||||||
|
@ -550,6 +691,7 @@ static void member_free(Member *m) {
|
||||||
free(m->name);
|
free(m->name);
|
||||||
free(m->signature);
|
free(m->signature);
|
||||||
free(m->result);
|
free(m->result);
|
||||||
|
free(m->value);
|
||||||
free(m);
|
free(m);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -758,16 +900,93 @@ static int introspect(sd_bus *bus, char **argv) {
|
||||||
if (r < 0)
|
if (r < 0)
|
||||||
return bus_log_parse_error(r);
|
return bus_log_parse_error(r);
|
||||||
|
|
||||||
|
/* First, get list of all properties */
|
||||||
r = parse_xml_introspect(argv[2], xml, &ops, members);
|
r = parse_xml_introspect(argv[2], xml, &ops, members);
|
||||||
if (r < 0)
|
if (r < 0)
|
||||||
return r;
|
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();
|
pager_open_if_enabled();
|
||||||
|
|
||||||
name_width = strlen("NAME");
|
name_width = strlen("NAME");
|
||||||
type_width = strlen("TYPE");
|
type_width = strlen("TYPE");
|
||||||
signature_width = strlen("SIGNATURE");
|
signature_width = strlen("SIGNATURE");
|
||||||
result_width = strlen("RESULT");
|
result_width = strlen("RESULT/VALUE");
|
||||||
|
|
||||||
sorted = newa(Member*, set_size(members));
|
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));
|
signature_width = MAX(signature_width, strlen(m->signature));
|
||||||
if (m->result)
|
if (m->result)
|
||||||
result_width = MAX(result_width, strlen(m->result));
|
result_width = MAX(result_width, strlen(m->result));
|
||||||
|
if (m->value)
|
||||||
|
result_width = MAX(result_width, strlen(m->value));
|
||||||
|
|
||||||
sorted[k++] = m;
|
sorted[k++] = m;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (result_width > 40)
|
||||||
|
result_width = 40;
|
||||||
|
|
||||||
assert(k == set_size(members));
|
assert(k == set_size(members));
|
||||||
qsort(sorted, k, sizeof(Member*), member_compare_funcp);
|
qsort(sorted, k, sizeof(Member*), member_compare_funcp);
|
||||||
|
|
||||||
printf("%-*s %-*s %-*s %-*s %s\n",
|
if (arg_legend) {
|
||||||
(int) name_width, "NAME",
|
printf("%-*s %-*s %-*s %-*s %s\n",
|
||||||
(int) type_width, "TYPE",
|
(int) name_width, "NAME",
|
||||||
(int) signature_width, "SIGNATURE",
|
(int) type_width, "TYPE",
|
||||||
(int) result_width, "RESULT",
|
(int) signature_width, "SIGNATURE",
|
||||||
"FLAGS");
|
(int) result_width, "RESULT/VALUE",
|
||||||
|
"FLAGS");
|
||||||
|
}
|
||||||
|
|
||||||
for (j = 0; j < k; j++) {
|
for (j = 0; j < k; j++) {
|
||||||
|
_cleanup_free_ char *ellipsized = NULL;
|
||||||
|
const char *rv;
|
||||||
bool is_interface;
|
bool is_interface;
|
||||||
|
|
||||||
m = sorted[j];
|
m = sorted[j];
|
||||||
|
|
||||||
is_interface = streq(m->type, "interface");
|
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",
|
printf("%s%s%-*s%s %-*s %-*s %-*s%s%s%s%s%s%s\n",
|
||||||
is_interface ? ansi_highlight() : "",
|
is_interface ? ansi_highlight() : "",
|
||||||
is_interface ? "" : ".",
|
is_interface ? "" : ".",
|
||||||
|
@ -810,7 +1047,7 @@ static int introspect(sd_bus *bus, char **argv) {
|
||||||
is_interface ? ansi_highlight_off() : "",
|
is_interface ? ansi_highlight_off() : "",
|
||||||
(int) type_width, strdash(m->type),
|
(int) type_width, strdash(m->type),
|
||||||
(int) signature_width, strdash(m->signature),
|
(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_DEPRECATED) ? " deprecated" : (m->flags || m->writable ? "" : " -"),
|
||||||
(m->flags & SD_BUS_VTABLE_METHOD_NO_REPLY) ? " no-reply" : "",
|
(m->flags & SD_BUS_VTABLE_METHOD_NO_REPLY) ? " no-reply" : "",
|
||||||
(m->flags & SD_BUS_VTABLE_PROPERTY_CONST) ? " const" : "",
|
(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);
|
r = sd_bus_message_is_empty(reply);
|
||||||
if (r < 0)
|
if (r < 0)
|
||||||
return bus_log_parse_error(r);
|
return bus_log_parse_error(r);
|
||||||
|
|
||||||
if (r == 0 && !arg_quiet) {
|
if (r == 0 && !arg_quiet) {
|
||||||
pager_open_if_enabled();
|
|
||||||
bus_message_dump(reply, stdout, 0);
|
if (arg_verbose) {
|
||||||
|
pager_open_if_enabled();
|
||||||
|
|
||||||
|
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;
|
return 0;
|
||||||
|
@ -1252,99 +1506,106 @@ static int call(sd_bus *bus, char *argv[]) {
|
||||||
static int get_property(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;
|
_cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
|
||||||
unsigned n;
|
unsigned n;
|
||||||
|
char **i;
|
||||||
int r;
|
int r;
|
||||||
|
|
||||||
assert(bus);
|
assert(bus);
|
||||||
|
|
||||||
n = strv_length(argv);
|
n = strv_length(argv);
|
||||||
if (n < 3) {
|
if (n < 5) {
|
||||||
log_error("Expects at least three arguments.");
|
log_error("Expects at least four arguments.");
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (n < 5) {
|
STRV_FOREACH(i, argv + 4) {
|
||||||
_cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
|
_cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
|
||||||
bool not_first = false;
|
const char *contents = NULL;
|
||||||
|
char type;
|
||||||
|
|
||||||
r = sd_bus_call_method(bus, argv[1], argv[2], "org.freedesktop.DBus.Properties", "GetAll", &error, &reply, "s", strempty(argv[3]));
|
r = sd_bus_call_method(bus, argv[1], argv[2], "org.freedesktop.DBus.Properties", "Get", &error, &reply, "ss", argv[3], *i);
|
||||||
if (r < 0) {
|
if (r < 0) {
|
||||||
log_error("%s", bus_error_message(&error, r));
|
log_error("%s", bus_error_message(&error, r));
|
||||||
return r;
|
return r;
|
||||||
}
|
}
|
||||||
|
|
||||||
r = sd_bus_message_enter_container(reply, 'a', "{sv}");
|
r = sd_bus_message_peek_type(reply, &type, &contents);
|
||||||
if (r < 0)
|
if (r < 0)
|
||||||
return bus_log_parse_error(r);
|
return bus_log_parse_error(r);
|
||||||
|
|
||||||
for (;;) {
|
r = sd_bus_message_enter_container(reply, 'v', contents);
|
||||||
const char *name;
|
if (r < 0)
|
||||||
|
return bus_log_parse_error(r);
|
||||||
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);
|
|
||||||
|
|
||||||
|
if (arg_verbose) {
|
||||||
pager_open_if_enabled();
|
pager_open_if_enabled();
|
||||||
bus_message_dump(reply, stdout, BUS_MESSAGE_DUMP_SUBTREE_ONLY);
|
|
||||||
|
|
||||||
r = sd_bus_message_exit_container(reply);
|
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)
|
if (r < 0)
|
||||||
return bus_log_parse_error(r);
|
return bus_log_parse_error(r);
|
||||||
|
|
||||||
r = sd_bus_message_exit_container(reply);
|
fputc('\n', stdout);
|
||||||
if (r < 0)
|
|
||||||
return bus_log_parse_error(r);
|
|
||||||
|
|
||||||
not_first = true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
r = sd_bus_message_exit_container(reply);
|
r = sd_bus_message_exit_container(reply);
|
||||||
if (r < 0)
|
if (r < 0)
|
||||||
return bus_log_parse_error(r);
|
return bus_log_parse_error(r);
|
||||||
} else {
|
}
|
||||||
char **i;
|
|
||||||
|
|
||||||
STRV_FOREACH(i, argv + 4) {
|
return 0;
|
||||||
_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);
|
static int set_property(sd_bus *bus, char *argv[]) {
|
||||||
if (r < 0) {
|
_cleanup_bus_message_unref_ sd_bus_message *m = NULL;
|
||||||
log_error("%s", bus_error_message(&error, r));
|
_cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
|
||||||
return r;
|
unsigned n;
|
||||||
}
|
char **p;
|
||||||
|
int r;
|
||||||
|
|
||||||
r = sd_bus_message_enter_container(reply, 'v', NULL);
|
assert(bus);
|
||||||
if (r < 0)
|
|
||||||
return bus_log_parse_error(r);
|
|
||||||
|
|
||||||
if (i > argv + 4)
|
n = strv_length(argv);
|
||||||
printf("\n");
|
if (n < 6) {
|
||||||
|
log_error("Expects at least five arguments.");
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
if (argv[5])
|
r = sd_bus_message_new_method_call(bus, &m, argv[1], argv[2], "org.freedesktop.DBus.Properties", "Set");
|
||||||
printf("Property %s:\n", *i);
|
if (r < 0)
|
||||||
|
return bus_log_create_error(r);
|
||||||
|
|
||||||
pager_open_if_enabled();
|
r = sd_bus_message_append(m, "ss", argv[3], argv[4]);
|
||||||
bus_message_dump(reply, stdout, BUS_MESSAGE_DUMP_SUBTREE_ONLY);
|
if (r < 0)
|
||||||
|
return bus_log_create_error(r);
|
||||||
|
|
||||||
r = sd_bus_message_exit_container(reply);
|
r = sd_bus_message_open_container(m, 'v', argv[5]);
|
||||||
if (r < 0)
|
if (r < 0)
|
||||||
return bus_log_parse_error(r);
|
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;
|
return 0;
|
||||||
|
@ -1368,18 +1629,21 @@ static int help(void) {
|
||||||
" --activatable Only show activatable names\n"
|
" --activatable Only show activatable names\n"
|
||||||
" --match=MATCH Only show matching messages\n"
|
" --match=MATCH Only show matching messages\n"
|
||||||
" --list Don't show tree, but simple object path list\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"
|
"Commands:\n"
|
||||||
" list List bus names\n"
|
" list List bus names\n"
|
||||||
" status SERVICE Show service name status\n"
|
" status SERVICE Show service name status\n"
|
||||||
" monitor [SERVICE...] Show bus traffic\n"
|
" monitor [SERVICE...] Show bus traffic\n"
|
||||||
" capture [SERVICE...] Capture bus traffic as pcap\n"
|
" capture [SERVICE...] Capture bus traffic as pcap\n"
|
||||||
" tree [SERVICE...] Show object tree of service\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 SERVICE OBJECT INTERFACE METHOD [SIGNATURE [ARGUMENT...]]\n"
|
||||||
" Call a method\n"
|
" Call a method\n"
|
||||||
" get-property SERVICE OBJECT [INTERFACE [PROPERTY...]]\n"
|
" get-property SERVICE OBJECT INTERFACE PROPERTY...\n"
|
||||||
" Get property value\n"
|
" Get property value\n"
|
||||||
|
" set-property SERVICE OBJECT INTERFACE PROPERTY SIGNATURE ARGUMENT...\n"
|
||||||
|
" Set property value\n"
|
||||||
" help Show this help\n"
|
" help Show this help\n"
|
||||||
, program_invocation_short_name);
|
, program_invocation_short_name);
|
||||||
|
|
||||||
|
@ -1402,6 +1666,7 @@ static int parse_argv(int argc, char *argv[]) {
|
||||||
ARG_ACTIVATABLE,
|
ARG_ACTIVATABLE,
|
||||||
ARG_SIZE,
|
ARG_SIZE,
|
||||||
ARG_LIST,
|
ARG_LIST,
|
||||||
|
ARG_VERBOSE,
|
||||||
};
|
};
|
||||||
|
|
||||||
static const struct option options[] = {
|
static const struct option options[] = {
|
||||||
|
@ -1422,6 +1687,7 @@ static int parse_argv(int argc, char *argv[]) {
|
||||||
{ "size", required_argument, NULL, ARG_SIZE },
|
{ "size", required_argument, NULL, ARG_SIZE },
|
||||||
{ "list", no_argument, NULL, ARG_LIST },
|
{ "list", no_argument, NULL, ARG_LIST },
|
||||||
{ "quiet", no_argument, NULL, 'q' },
|
{ "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;
|
arg_quiet = true;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case ARG_VERBOSE:
|
||||||
|
arg_verbose = true;
|
||||||
|
break;
|
||||||
|
|
||||||
case '?':
|
case '?':
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
|
|
||||||
|
@ -1557,6 +1827,9 @@ static int busctl_main(sd_bus *bus, int argc, char *argv[]) {
|
||||||
if (streq(argv[optind], "get-property"))
|
if (streq(argv[optind], "get-property"))
|
||||||
return get_property(bus, argv + optind);
|
return get_property(bus, argv + optind);
|
||||||
|
|
||||||
|
if (streq(argv[optind], "set-property"))
|
||||||
|
return set_property(bus, argv + optind);
|
||||||
|
|
||||||
if (streq(argv[optind], "help"))
|
if (streq(argv[optind], "help"))
|
||||||
return help();
|
return help();
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue