2017-11-18 17:09:20 +01:00
|
|
|
/* SPDX-License-Identifier: LGPL-2.1+ */
|
2013-03-19 20:03:16 +01:00
|
|
|
|
2013-04-24 17:56:28 +02:00
|
|
|
#include <getopt.h>
|
2017-12-11 19:50:30 +01:00
|
|
|
#include <stdio_ext.h>
|
2013-04-24 17:56:28 +02:00
|
|
|
|
2013-03-19 20:03:16 +01:00
|
|
|
#include "sd-bus.h"
|
2015-09-23 03:01:06 +02:00
|
|
|
|
2015-10-27 03:01:06 +01:00
|
|
|
#include "alloc-util.h"
|
2013-11-12 00:12:08 +01:00
|
|
|
#include "bus-dump.h"
|
2015-09-23 03:01:06 +02:00
|
|
|
#include "bus-internal.h"
|
2014-11-14 13:11:10 +01:00
|
|
|
#include "bus-signature.h"
|
2014-11-20 20:59:57 +01:00
|
|
|
#include "bus-type.h"
|
2015-09-23 03:01:06 +02:00
|
|
|
#include "bus-util.h"
|
2014-11-19 16:42:21 +01:00
|
|
|
#include "busctl-introspect.h"
|
2015-10-23 18:52:53 +02:00
|
|
|
#include "escape.h"
|
2015-10-25 13:14:12 +01:00
|
|
|
#include "fd-util.h"
|
2018-06-14 14:53:46 +02:00
|
|
|
#include "fileio.h"
|
2018-07-04 15:28:09 +02:00
|
|
|
#include "json.h"
|
2015-10-26 23:01:30 +01:00
|
|
|
#include "locale-util.h"
|
2015-09-23 03:01:06 +02:00
|
|
|
#include "log.h"
|
|
|
|
#include "pager.h"
|
2015-10-26 16:18:16 +01:00
|
|
|
#include "parse-util.h"
|
2015-09-23 03:01:06 +02:00
|
|
|
#include "path-util.h"
|
|
|
|
#include "set.h"
|
|
|
|
#include "strv.h"
|
2015-04-10 23:15:59 +02:00
|
|
|
#include "terminal-util.h"
|
2015-10-25 22:32:30 +01:00
|
|
|
#include "user-util.h"
|
2015-09-23 03:01:06 +02:00
|
|
|
#include "util.h"
|
2018-03-13 21:09:16 +01:00
|
|
|
#include "verbs.h"
|
2013-03-19 20:03:16 +01:00
|
|
|
|
2018-07-04 15:28:09 +02:00
|
|
|
static enum {
|
|
|
|
JSON_OFF,
|
|
|
|
JSON_SHORT,
|
|
|
|
JSON_PRETTY,
|
|
|
|
} arg_json = JSON_OFF;
|
2013-04-24 17:56:28 +02:00
|
|
|
static bool arg_no_pager = false;
|
2014-02-19 17:35:35 +01:00
|
|
|
static bool arg_legend = true;
|
2018-06-21 18:51:49 +02:00
|
|
|
static const char *arg_address = NULL;
|
2013-12-24 18:15:38 +01:00
|
|
|
static bool arg_unique = false;
|
|
|
|
static bool arg_acquired = false;
|
|
|
|
static bool arg_activatable = false;
|
|
|
|
static bool arg_show_machine = false;
|
2013-04-24 17:56:28 +02:00
|
|
|
static char **arg_matches = NULL;
|
2013-11-06 17:33:24 +01:00
|
|
|
static BusTransport arg_transport = BUS_TRANSPORT_LOCAL;
|
2018-06-14 14:53:59 +02:00
|
|
|
static const char *arg_host = NULL;
|
2013-11-06 17:33:24 +01:00
|
|
|
static bool arg_user = false;
|
2014-10-30 01:13:11 +01:00
|
|
|
static size_t arg_snaplen = 4096;
|
2014-11-10 19:24:48 +01:00
|
|
|
static bool arg_list = false;
|
2014-11-14 13:11:10 +01:00
|
|
|
static bool arg_quiet = false;
|
2014-11-20 23:12:29 +01:00
|
|
|
static bool arg_verbose = false;
|
2014-11-21 20:13:26 +01:00
|
|
|
static bool arg_expect_reply = true;
|
|
|
|
static bool arg_auto_start = true;
|
|
|
|
static bool arg_allow_interactive_authorization = true;
|
2014-11-24 21:53:29 +01:00
|
|
|
static bool arg_augment_creds = true;
|
2017-12-15 22:19:34 +01:00
|
|
|
static bool arg_watch_bind = false;
|
2014-11-21 20:31:50 +01:00
|
|
|
static usec_t arg_timeout = 0;
|
2013-04-24 17:56:28 +02:00
|
|
|
|
2014-11-24 21:39:18 +01:00
|
|
|
#define NAME_IS_ACQUIRED INT_TO_PTR(1)
|
|
|
|
#define NAME_IS_ACTIVATABLE INT_TO_PTR(2)
|
|
|
|
|
2018-03-13 21:09:16 +01:00
|
|
|
static int acquire_bus(bool set_monitor, sd_bus **ret) {
|
|
|
|
_cleanup_(sd_bus_unrefp) sd_bus *bus = NULL;
|
|
|
|
int r;
|
|
|
|
|
|
|
|
r = sd_bus_new(&bus);
|
|
|
|
if (r < 0)
|
|
|
|
return log_error_errno(r, "Failed to allocate bus: %m");
|
|
|
|
|
|
|
|
if (set_monitor) {
|
|
|
|
r = sd_bus_set_monitor(bus, true);
|
|
|
|
if (r < 0)
|
|
|
|
return log_error_errno(r, "Failed to set monitor mode: %m");
|
|
|
|
|
|
|
|
r = sd_bus_negotiate_creds(bus, true, _SD_BUS_CREDS_ALL);
|
|
|
|
if (r < 0)
|
|
|
|
return log_error_errno(r, "Failed to enable credentials: %m");
|
|
|
|
|
|
|
|
r = sd_bus_negotiate_timestamp(bus, true);
|
|
|
|
if (r < 0)
|
|
|
|
return log_error_errno(r, "Failed to enable timestamps: %m");
|
|
|
|
|
|
|
|
r = sd_bus_negotiate_fds(bus, true);
|
|
|
|
if (r < 0)
|
|
|
|
return log_error_errno(r, "Failed to enable fds: %m");
|
|
|
|
}
|
|
|
|
|
|
|
|
r = sd_bus_set_bus_client(bus, true);
|
|
|
|
if (r < 0)
|
|
|
|
return log_error_errno(r, "Failed to set bus client: %m");
|
|
|
|
|
|
|
|
r = sd_bus_set_watch_bind(bus, arg_watch_bind);
|
|
|
|
if (r < 0)
|
|
|
|
return log_error_errno(r, "Failed to set watch-bind setting to '%s': %m", yes_no(arg_watch_bind));
|
|
|
|
|
|
|
|
if (arg_address)
|
|
|
|
r = sd_bus_set_address(bus, arg_address);
|
|
|
|
else {
|
|
|
|
switch (arg_transport) {
|
|
|
|
|
|
|
|
case BUS_TRANSPORT_LOCAL:
|
|
|
|
if (arg_user) {
|
|
|
|
bus->is_user = true;
|
|
|
|
r = bus_set_address_user(bus);
|
|
|
|
} else {
|
|
|
|
bus->is_system = true;
|
|
|
|
r = bus_set_address_system(bus);
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
case BUS_TRANSPORT_REMOTE:
|
|
|
|
r = bus_set_address_system_remote(bus, arg_host);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case BUS_TRANSPORT_MACHINE:
|
|
|
|
r = bus_set_address_system_machine(bus, arg_host);
|
|
|
|
break;
|
|
|
|
|
|
|
|
default:
|
|
|
|
assert_not_reached("Hmm, unknown transport type.");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (r < 0)
|
|
|
|
return log_error_errno(r, "Failed to set address: %m");
|
|
|
|
|
|
|
|
r = sd_bus_start(bus);
|
|
|
|
if (r < 0)
|
|
|
|
return log_error_errno(r, "Failed to connect to bus: %m");
|
|
|
|
|
2018-04-05 07:26:26 +02:00
|
|
|
*ret = TAKE_PTR(bus);
|
2018-03-13 21:09:16 +01:00
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int list_bus_names(int argc, char **argv, void *userdata) {
|
|
|
|
_cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL;
|
2013-12-03 18:42:51 +01:00
|
|
|
_cleanup_strv_free_ char **acquired = NULL, **activatable = NULL;
|
2013-12-18 02:49:03 +01:00
|
|
|
_cleanup_free_ char **merged = NULL;
|
|
|
|
_cleanup_hashmap_free_ Hashmap *names = NULL;
|
2013-03-19 20:03:16 +01:00
|
|
|
char **i;
|
|
|
|
int r;
|
2013-03-20 22:56:25 +01:00
|
|
|
size_t max_i = 0;
|
2013-12-18 02:49:03 +01:00
|
|
|
unsigned n = 0;
|
|
|
|
void *v;
|
|
|
|
char *k;
|
|
|
|
Iterator iterator;
|
2013-03-19 20:03:16 +01:00
|
|
|
|
2014-11-10 19:24:48 +01:00
|
|
|
if (!arg_unique && !arg_acquired && !arg_activatable)
|
|
|
|
arg_unique = arg_acquired = arg_activatable = true;
|
|
|
|
|
2018-03-13 21:09:16 +01:00
|
|
|
r = acquire_bus(false, &bus);
|
|
|
|
if (r < 0)
|
|
|
|
return r;
|
|
|
|
|
2013-12-24 18:15:38 +01:00
|
|
|
r = sd_bus_list_names(bus, (arg_acquired || arg_unique) ? &acquired : NULL, arg_activatable ? &activatable : NULL);
|
2014-11-28 18:23:20 +01:00
|
|
|
if (r < 0)
|
|
|
|
return log_error_errno(r, "Failed to list names: %m");
|
2013-03-19 20:03:16 +01:00
|
|
|
|
2018-03-14 09:48:29 +01:00
|
|
|
(void) pager_open(arg_no_pager, false);
|
2013-04-24 17:56:28 +02:00
|
|
|
|
2014-08-13 01:00:18 +02:00
|
|
|
names = hashmap_new(&string_hash_ops);
|
2013-12-18 02:49:03 +01:00
|
|
|
if (!names)
|
|
|
|
return log_oom();
|
2013-03-20 22:56:25 +01:00
|
|
|
|
2013-12-18 02:49:03 +01:00
|
|
|
STRV_FOREACH(i, acquired) {
|
2013-12-03 18:42:51 +01:00
|
|
|
max_i = MAX(max_i, strlen(*i));
|
|
|
|
|
2014-11-24 21:39:18 +01:00
|
|
|
r = hashmap_put(names, *i, NAME_IS_ACQUIRED);
|
2014-11-28 18:23:20 +01:00
|
|
|
if (r < 0)
|
|
|
|
return log_error_errno(r, "Failed to add to hashmap: %m");
|
2013-12-18 02:49:03 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
STRV_FOREACH(i, activatable) {
|
2013-03-20 22:56:25 +01:00
|
|
|
max_i = MAX(max_i, strlen(*i));
|
|
|
|
|
2014-11-24 21:39:18 +01:00
|
|
|
r = hashmap_put(names, *i, NAME_IS_ACTIVATABLE);
|
2014-11-28 18:23:20 +01:00
|
|
|
if (r < 0 && r != -EEXIST)
|
|
|
|
return log_error_errno(r, "Failed to add to hashmap: %m");
|
2013-12-18 02:49:03 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
merged = new(char*, hashmap_size(names) + 1);
|
2018-03-12 09:45:42 +01:00
|
|
|
if (!merged)
|
|
|
|
return log_oom();
|
|
|
|
|
2013-12-18 02:49:03 +01:00
|
|
|
HASHMAP_FOREACH_KEY(v, k, names, iterator)
|
|
|
|
merged[n++] = k;
|
|
|
|
|
|
|
|
merged[n] = NULL;
|
|
|
|
strv_sort(merged);
|
|
|
|
|
2014-02-19 17:35:35 +01:00
|
|
|
if (arg_legend) {
|
|
|
|
printf("%-*s %*s %-*s %-*s %-*s %-*s %-*s %-*s",
|
2014-11-04 16:13:49 +01:00
|
|
|
(int) max_i, "NAME", 10, "PID", 15, "PROCESS", 16, "USER", 13, "CONNECTION", 25, "UNIT", 10, "SESSION", 19, "DESCRIPTION");
|
2013-03-20 22:56:25 +01:00
|
|
|
|
2014-02-19 17:35:35 +01:00
|
|
|
if (arg_show_machine)
|
|
|
|
puts(" MACHINE");
|
|
|
|
else
|
|
|
|
putchar('\n');
|
|
|
|
}
|
2013-11-28 20:41:55 +01:00
|
|
|
|
2013-12-18 02:49:03 +01:00
|
|
|
STRV_FOREACH(i, merged) {
|
tree-wide: expose "p"-suffix unref calls in public APIs to make gcc cleanup easy
GLIB has recently started to officially support the gcc cleanup
attribute in its public API, hence let's do the same for our APIs.
With this patch we'll define an xyz_unrefp() call for each public
xyz_unref() call, to make it easy to use inside a
__attribute__((cleanup())) expression. Then, all code is ported over to
make use of this.
The new calls are also documented in the man pages, with examples how to
use them (well, I only added docs where the _unref() call itself already
had docs, and the examples, only cover sd_bus_unrefp() and
sd_event_unrefp()).
This also renames sd_lldp_free() to sd_lldp_unref(), since that's how we
tend to call our destructors these days.
Note that this defines no public macro that wraps gcc's attribute and
makes it easier to use. While I think it's our duty in the library to
make our stuff easy to use, I figure it's not our duty to make gcc's own
features easy to use on its own. Most likely, client code which wants to
make use of this should define its own:
#define _cleanup_(function) __attribute__((cleanup(function)))
Or similar, to make the gcc feature easier to use.
Making this logic public has the benefit that we can remove three header
files whose only purpose was to define these functions internally.
See #2008.
2015-11-27 19:13:45 +01:00
|
|
|
_cleanup_(sd_bus_creds_unrefp) sd_bus_creds *creds = NULL;
|
2013-12-18 02:49:03 +01:00
|
|
|
sd_id128_t mid;
|
2013-12-03 18:42:51 +01:00
|
|
|
|
2014-11-24 21:39:18 +01:00
|
|
|
if (hashmap_get(names, *i) == NAME_IS_ACTIVATABLE) {
|
2013-12-18 02:49:03 +01:00
|
|
|
/* Activatable */
|
2013-12-03 18:42:51 +01:00
|
|
|
|
2013-12-18 02:49:03 +01:00
|
|
|
printf("%-*s", (int) max_i, *i);
|
2013-12-24 18:15:38 +01:00
|
|
|
printf(" - - - (activatable) - - ");
|
|
|
|
if (arg_show_machine)
|
2013-12-18 02:49:03 +01:00
|
|
|
puts(" -");
|
2013-12-24 18:15:38 +01:00
|
|
|
else
|
|
|
|
putchar('\n');
|
2013-12-03 18:42:51 +01:00
|
|
|
continue;
|
|
|
|
|
2013-12-18 02:49:03 +01:00
|
|
|
}
|
2013-03-19 20:03:16 +01:00
|
|
|
|
2013-12-24 18:15:38 +01:00
|
|
|
if (!arg_unique && (*i)[0] == ':')
|
|
|
|
continue;
|
|
|
|
|
|
|
|
if (!arg_acquired && (*i)[0] != ':')
|
2013-04-24 17:56:28 +02:00
|
|
|
continue;
|
2013-03-20 22:56:25 +01:00
|
|
|
|
|
|
|
printf("%-*s", (int) max_i, *i);
|
2013-03-19 20:03:16 +01:00
|
|
|
|
2014-11-24 21:53:29 +01:00
|
|
|
r = sd_bus_get_name_creds(
|
|
|
|
bus, *i,
|
|
|
|
(arg_augment_creds ? SD_BUS_CREDS_AUGMENT : 0) |
|
bus: use EUID over UID and fix unix-creds
Whenever a process performs an action on an object, the kernel uses the
EUID of the process to do permission checks and to apply on any newly
created objects. The UID of a process is only used if someone *ELSE* acts
on the process. That is, the UID of a process defines who owns the
process, the EUID defines what privileges are used by this process when
performing an action.
Process limits, on the other hand, are always applied to the real UID, not
the effective UID. This is, because a process has a user object linked,
which always corresponds to its UID. A process never has a user object
linked for its EUID. Thus, accounting (and limits) is always done on the
real UID.
This commit fixes all sd-bus users to use the EUID when performing
privilege checks and alike. Furthermore, it fixes unix-creds to be parsed
as EUID, not UID (as the kernel always takes the EUID on UDS). Anyone
using UID (eg., to do user-accounting) has to fall back to the EUID as UDS
does not transmit the UID.
2015-01-18 13:55:55 +01:00
|
|
|
SD_BUS_CREDS_EUID|SD_BUS_CREDS_PID|SD_BUS_CREDS_COMM|
|
2014-11-24 21:53:29 +01:00
|
|
|
SD_BUS_CREDS_UNIQUE_NAME|SD_BUS_CREDS_UNIT|SD_BUS_CREDS_SESSION|
|
|
|
|
SD_BUS_CREDS_DESCRIPTION, &creds);
|
2013-03-20 22:56:25 +01:00
|
|
|
if (r >= 0) {
|
2014-01-22 18:43:03 +01:00
|
|
|
const char *unique, *session, *unit, *cn;
|
2013-11-28 17:50:02 +01:00
|
|
|
pid_t pid;
|
|
|
|
uid_t uid;
|
2013-03-19 20:03:16 +01:00
|
|
|
|
2013-11-28 17:50:02 +01:00
|
|
|
r = sd_bus_creds_get_pid(creds, &pid);
|
|
|
|
if (r >= 0) {
|
|
|
|
const char *comm = NULL;
|
2013-03-19 20:03:16 +01:00
|
|
|
|
2013-11-28 17:50:02 +01:00
|
|
|
sd_bus_creds_get_comm(creds, &comm);
|
2013-03-20 22:56:25 +01:00
|
|
|
|
2013-11-28 17:50:02 +01:00
|
|
|
printf(" %10lu %-15s", (unsigned long) pid, strna(comm));
|
|
|
|
} else
|
2013-11-28 20:41:55 +01:00
|
|
|
fputs(" - - ", stdout);
|
2013-03-20 22:56:25 +01:00
|
|
|
|
bus: use EUID over UID and fix unix-creds
Whenever a process performs an action on an object, the kernel uses the
EUID of the process to do permission checks and to apply on any newly
created objects. The UID of a process is only used if someone *ELSE* acts
on the process. That is, the UID of a process defines who owns the
process, the EUID defines what privileges are used by this process when
performing an action.
Process limits, on the other hand, are always applied to the real UID, not
the effective UID. This is, because a process has a user object linked,
which always corresponds to its UID. A process never has a user object
linked for its EUID. Thus, accounting (and limits) is always done on the
real UID.
This commit fixes all sd-bus users to use the EUID when performing
privilege checks and alike. Furthermore, it fixes unix-creds to be parsed
as EUID, not UID (as the kernel always takes the EUID on UDS). Anyone
using UID (eg., to do user-accounting) has to fall back to the EUID as UDS
does not transmit the UID.
2015-01-18 13:55:55 +01:00
|
|
|
r = sd_bus_creds_get_euid(creds, &uid);
|
2013-11-28 17:50:02 +01:00
|
|
|
if (r >= 0) {
|
|
|
|
_cleanup_free_ char *u = NULL;
|
2013-03-20 22:56:25 +01:00
|
|
|
|
2013-11-28 17:50:02 +01:00
|
|
|
u = uid_to_name(uid);
|
|
|
|
if (!u)
|
|
|
|
return log_oom();
|
2013-03-20 22:56:25 +01:00
|
|
|
|
2013-11-28 17:50:02 +01:00
|
|
|
if (strlen(u) > 16)
|
|
|
|
u[16] = 0;
|
|
|
|
|
|
|
|
printf(" %-16s", u);
|
|
|
|
} else
|
2013-11-28 20:41:55 +01:00
|
|
|
fputs(" - ", stdout);
|
2013-03-20 22:56:25 +01:00
|
|
|
|
2013-11-30 04:14:10 +01:00
|
|
|
r = sd_bus_creds_get_unique_name(creds, &unique);
|
|
|
|
if (r >= 0)
|
2013-12-24 18:15:38 +01:00
|
|
|
printf(" %-13s", unique);
|
|
|
|
else
|
|
|
|
fputs(" - ", stdout);
|
|
|
|
|
|
|
|
r = sd_bus_creds_get_unit(creds, &unit);
|
|
|
|
if (r >= 0) {
|
|
|
|
_cleanup_free_ char *e;
|
|
|
|
|
|
|
|
e = ellipsize(unit, 25, 100);
|
|
|
|
if (!e)
|
|
|
|
return log_oom();
|
|
|
|
|
|
|
|
printf(" %-25s", e);
|
|
|
|
} else
|
|
|
|
fputs(" - ", stdout);
|
|
|
|
|
|
|
|
r = sd_bus_creds_get_session(creds, &session);
|
|
|
|
if (r >= 0)
|
|
|
|
printf(" %-10s", session);
|
2013-11-28 20:41:55 +01:00
|
|
|
else
|
2013-12-24 18:15:38 +01:00
|
|
|
fputs(" - ", stdout);
|
2013-11-11 23:45:36 +01:00
|
|
|
|
2014-11-04 16:13:49 +01:00
|
|
|
r = sd_bus_creds_get_description(creds, &cn);
|
2014-01-22 18:43:03 +01:00
|
|
|
if (r >= 0)
|
|
|
|
printf(" %-19s", cn);
|
|
|
|
else
|
|
|
|
fputs(" - ", stdout);
|
|
|
|
|
2013-11-11 23:45:36 +01:00
|
|
|
} else
|
2014-01-22 18:43:03 +01:00
|
|
|
printf(" - - - - - - - ");
|
2013-11-28 20:41:55 +01:00
|
|
|
|
2013-12-24 18:15:38 +01:00
|
|
|
if (arg_show_machine) {
|
2014-10-22 19:17:24 +02:00
|
|
|
r = sd_bus_get_name_machine_id(bus, *i, &mid);
|
2013-11-28 20:41:55 +01:00
|
|
|
if (r >= 0) {
|
|
|
|
char m[SD_ID128_STRING_MAX];
|
|
|
|
printf(" %s\n", sd_id128_to_string(mid, m));
|
|
|
|
} else
|
|
|
|
puts(" -");
|
2013-12-24 18:15:38 +01:00
|
|
|
} else
|
|
|
|
putchar('\n');
|
2013-03-19 20:03:16 +01:00
|
|
|
}
|
|
|
|
|
2013-04-24 17:56:28 +02:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2014-11-10 19:24:48 +01:00
|
|
|
static void print_subtree(const char *prefix, const char *path, char **l) {
|
|
|
|
const char *vertical, *space;
|
|
|
|
char **n;
|
|
|
|
|
|
|
|
/* We assume the list is sorted. Let's first skip over the
|
|
|
|
* entry we are looking at. */
|
|
|
|
for (;;) {
|
|
|
|
if (!*l)
|
|
|
|
return;
|
|
|
|
|
|
|
|
if (!streq(*l, path))
|
|
|
|
break;
|
|
|
|
|
|
|
|
l++;
|
|
|
|
}
|
|
|
|
|
2016-05-07 23:30:18 +02:00
|
|
|
vertical = strjoina(prefix, special_glyph(TREE_VERTICAL));
|
|
|
|
space = strjoina(prefix, special_glyph(TREE_SPACE));
|
2014-11-10 19:24:48 +01:00
|
|
|
|
|
|
|
for (;;) {
|
|
|
|
bool has_more = false;
|
|
|
|
|
|
|
|
if (!*l || !path_startswith(*l, path))
|
|
|
|
break;
|
|
|
|
|
|
|
|
n = l + 1;
|
|
|
|
for (;;) {
|
|
|
|
if (!*n || !path_startswith(*n, path))
|
|
|
|
break;
|
|
|
|
|
|
|
|
if (!path_startswith(*n, *l)) {
|
|
|
|
has_more = true;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
n++;
|
|
|
|
}
|
|
|
|
|
2016-05-07 23:30:18 +02:00
|
|
|
printf("%s%s%s\n", prefix, special_glyph(has_more ? TREE_BRANCH : TREE_RIGHT), *l);
|
2014-11-10 19:24:48 +01:00
|
|
|
|
|
|
|
print_subtree(has_more ? vertical : space, *l, l);
|
|
|
|
l = n;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static void print_tree(const char *prefix, char **l) {
|
|
|
|
|
|
|
|
prefix = strempty(prefix);
|
|
|
|
|
|
|
|
if (arg_list) {
|
|
|
|
char **i;
|
|
|
|
|
|
|
|
STRV_FOREACH(i, l)
|
|
|
|
printf("%s%s\n", prefix, *i);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2014-11-14 13:18:33 +01:00
|
|
|
if (strv_isempty(l)) {
|
|
|
|
printf("No objects discovered.\n");
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (streq(l[0], "/") && !l[1]) {
|
|
|
|
printf("Only root object discovered.\n");
|
|
|
|
return;
|
|
|
|
}
|
2014-11-10 19:24:48 +01:00
|
|
|
|
|
|
|
print_subtree(prefix, "/", l);
|
|
|
|
}
|
|
|
|
|
2014-11-19 16:42:21 +01:00
|
|
|
static int on_path(const char *path, void *userdata) {
|
|
|
|
Set *paths = userdata;
|
2014-11-10 19:24:48 +01:00
|
|
|
int r;
|
|
|
|
|
2014-11-19 16:42:21 +01:00
|
|
|
assert(paths);
|
2014-11-10 19:24:48 +01:00
|
|
|
|
2014-11-19 16:42:21 +01:00
|
|
|
r = set_put_strdup(paths, path);
|
|
|
|
if (r < 0)
|
|
|
|
return log_oom();
|
2014-11-10 19:24:48 +01:00
|
|
|
|
2014-11-19 16:42:21 +01:00
|
|
|
return 0;
|
2014-11-10 19:24:48 +01:00
|
|
|
}
|
|
|
|
|
2014-11-20 00:00:22 +01:00
|
|
|
static int find_nodes(sd_bus *bus, const char *service, const char *path, Set *paths, bool many) {
|
2014-11-19 21:12:54 +01:00
|
|
|
static const XMLIntrospectOps ops = {
|
2014-11-19 16:42:21 +01:00
|
|
|
.on_path = on_path,
|
|
|
|
};
|
|
|
|
|
tree-wide: expose "p"-suffix unref calls in public APIs to make gcc cleanup easy
GLIB has recently started to officially support the gcc cleanup
attribute in its public API, hence let's do the same for our APIs.
With this patch we'll define an xyz_unrefp() call for each public
xyz_unref() call, to make it easy to use inside a
__attribute__((cleanup())) expression. Then, all code is ported over to
make use of this.
The new calls are also documented in the man pages, with examples how to
use them (well, I only added docs where the _unref() call itself already
had docs, and the examples, only cover sd_bus_unrefp() and
sd_event_unrefp()).
This also renames sd_lldp_free() to sd_lldp_unref(), since that's how we
tend to call our destructors these days.
Note that this defines no public macro that wraps gcc's attribute and
makes it easier to use. While I think it's our duty in the library to
make our stuff easy to use, I figure it's not our duty to make gcc's own
features easy to use on its own. Most likely, client code which wants to
make use of this should define its own:
#define _cleanup_(function) __attribute__((cleanup(function)))
Or similar, to make the gcc feature easier to use.
Making this logic public has the benefit that we can remove three header
files whose only purpose was to define these functions internally.
See #2008.
2015-11-27 19:13:45 +01:00
|
|
|
_cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
|
|
|
|
_cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
|
2014-11-19 16:42:21 +01:00
|
|
|
const char *xml;
|
2014-11-10 19:24:48 +01:00
|
|
|
int r;
|
|
|
|
|
|
|
|
r = sd_bus_call_method(bus, service, path, "org.freedesktop.DBus.Introspectable", "Introspect", &error, &reply, "");
|
|
|
|
if (r < 0) {
|
2014-11-20 00:00:22 +01:00
|
|
|
if (many)
|
|
|
|
printf("Failed to introspect object %s of service %s: %s\n", path, service, bus_error_message(&error, r));
|
|
|
|
else
|
2018-06-14 14:54:32 +02:00
|
|
|
log_error_errno(r, "Failed to introspect object %s of service %s: %s", path, service, bus_error_message(&error, r));
|
2014-11-10 19:24:48 +01:00
|
|
|
return r;
|
|
|
|
}
|
|
|
|
|
|
|
|
r = sd_bus_message_read(reply, "s", &xml);
|
|
|
|
if (r < 0)
|
|
|
|
return bus_log_parse_error(r);
|
|
|
|
|
2014-11-19 16:42:21 +01:00
|
|
|
return parse_xml_introspect(path, xml, &ops, paths);
|
2014-11-10 19:24:48 +01:00
|
|
|
}
|
|
|
|
|
2014-11-20 00:00:22 +01:00
|
|
|
static int tree_one(sd_bus *bus, const char *service, const char *prefix, bool many) {
|
2014-11-10 19:24:48 +01:00
|
|
|
_cleanup_set_free_free_ Set *paths = NULL, *done = NULL, *failed = NULL;
|
|
|
|
_cleanup_free_ char **l = NULL;
|
|
|
|
char *m;
|
|
|
|
int r;
|
|
|
|
|
|
|
|
paths = set_new(&string_hash_ops);
|
|
|
|
if (!paths)
|
|
|
|
return log_oom();
|
|
|
|
|
|
|
|
done = set_new(&string_hash_ops);
|
|
|
|
if (!done)
|
|
|
|
return log_oom();
|
|
|
|
|
|
|
|
failed = set_new(&string_hash_ops);
|
|
|
|
if (!failed)
|
|
|
|
return log_oom();
|
|
|
|
|
|
|
|
m = strdup("/");
|
|
|
|
if (!m)
|
|
|
|
return log_oom();
|
|
|
|
|
|
|
|
r = set_put(paths, m);
|
|
|
|
if (r < 0) {
|
|
|
|
free(m);
|
|
|
|
return log_oom();
|
|
|
|
}
|
|
|
|
|
|
|
|
for (;;) {
|
|
|
|
_cleanup_free_ char *p = NULL;
|
|
|
|
int q;
|
|
|
|
|
|
|
|
p = set_steal_first(paths);
|
|
|
|
if (!p)
|
|
|
|
break;
|
|
|
|
|
|
|
|
if (set_contains(done, p) ||
|
|
|
|
set_contains(failed, p))
|
|
|
|
continue;
|
|
|
|
|
2014-11-20 00:00:22 +01:00
|
|
|
q = find_nodes(bus, service, p, paths, many);
|
2014-11-10 19:24:48 +01:00
|
|
|
if (q < 0) {
|
|
|
|
if (r >= 0)
|
|
|
|
r = q;
|
|
|
|
|
|
|
|
q = set_put(failed, p);
|
|
|
|
} else
|
|
|
|
q = set_put(done, p);
|
|
|
|
|
|
|
|
if (q < 0)
|
|
|
|
return log_oom();
|
|
|
|
|
|
|
|
assert(q != 0);
|
|
|
|
p = NULL;
|
|
|
|
}
|
|
|
|
|
2018-03-14 09:48:29 +01:00
|
|
|
(void) pager_open(arg_no_pager, false);
|
2014-11-20 00:00:22 +01:00
|
|
|
|
2014-11-10 19:24:48 +01:00
|
|
|
l = set_get_strv(done);
|
|
|
|
if (!l)
|
|
|
|
return log_oom();
|
|
|
|
|
|
|
|
strv_sort(l);
|
|
|
|
print_tree(prefix, l);
|
|
|
|
|
|
|
|
fflush(stdout);
|
|
|
|
|
|
|
|
return r;
|
|
|
|
}
|
|
|
|
|
2018-03-13 21:09:16 +01:00
|
|
|
static int tree(int argc, char **argv, void *userdata) {
|
|
|
|
_cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL;
|
2014-11-10 19:24:48 +01:00
|
|
|
char **i;
|
|
|
|
int r = 0;
|
|
|
|
|
|
|
|
if (!arg_unique && !arg_acquired)
|
|
|
|
arg_acquired = true;
|
|
|
|
|
2018-03-13 21:09:16 +01:00
|
|
|
r = acquire_bus(false, &bus);
|
|
|
|
if (r < 0)
|
|
|
|
return r;
|
|
|
|
|
|
|
|
if (argc <= 1) {
|
2014-11-10 19:24:48 +01:00
|
|
|
_cleanup_strv_free_ char **names = NULL;
|
2014-11-14 13:18:33 +01:00
|
|
|
bool not_first = false;
|
2014-11-10 19:24:48 +01:00
|
|
|
|
|
|
|
r = sd_bus_list_names(bus, &names, NULL);
|
2014-11-28 18:23:20 +01:00
|
|
|
if (r < 0)
|
|
|
|
return log_error_errno(r, "Failed to get name list: %m");
|
2014-11-10 19:24:48 +01:00
|
|
|
|
2018-03-14 09:48:29 +01:00
|
|
|
(void) pager_open(arg_no_pager, false);
|
2014-11-10 19:24:48 +01:00
|
|
|
|
|
|
|
STRV_FOREACH(i, names) {
|
|
|
|
int q;
|
|
|
|
|
|
|
|
if (!arg_unique && (*i)[0] == ':')
|
|
|
|
continue;
|
|
|
|
|
|
|
|
if (!arg_acquired && (*i)[0] == ':')
|
|
|
|
continue;
|
|
|
|
|
|
|
|
if (not_first)
|
|
|
|
printf("\n");
|
|
|
|
|
2015-09-19 00:45:05 +02:00
|
|
|
printf("Service %s%s%s:\n", ansi_highlight(), *i, ansi_normal());
|
2014-11-10 19:24:48 +01:00
|
|
|
|
2014-11-20 00:00:22 +01:00
|
|
|
q = tree_one(bus, *i, NULL, true);
|
2014-11-10 19:24:48 +01:00
|
|
|
if (q < 0 && r >= 0)
|
|
|
|
r = q;
|
|
|
|
|
|
|
|
not_first = true;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
STRV_FOREACH(i, argv+1) {
|
|
|
|
int q;
|
|
|
|
|
|
|
|
if (i > argv+1)
|
|
|
|
printf("\n");
|
|
|
|
|
2014-11-20 00:00:22 +01:00
|
|
|
if (argv[2]) {
|
2018-03-14 09:48:29 +01:00
|
|
|
(void) pager_open(arg_no_pager, false);
|
2015-09-19 00:45:05 +02:00
|
|
|
printf("Service %s%s%s:\n", ansi_highlight(), *i, ansi_normal());
|
2014-11-20 00:00:22 +01:00
|
|
|
}
|
2014-11-10 19:24:48 +01:00
|
|
|
|
2014-11-20 00:00:22 +01:00
|
|
|
q = tree_one(bus, *i, NULL, !!argv[2]);
|
2014-11-10 19:24:48 +01:00
|
|
|
if (q < 0 && r >= 0)
|
|
|
|
r = q;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return r;
|
|
|
|
}
|
|
|
|
|
2014-11-20 23:12:29 +01:00
|
|
|
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);
|
2016-02-13 20:32:11 +01:00
|
|
|
if (r < 0)
|
2014-11-20 23:12:29 +01:00
|
|
|
return r;
|
2016-02-13 20:32:11 +01:00
|
|
|
if (r == 0)
|
|
|
|
return needs_space;
|
2014-11-20 23:12:29 +01:00
|
|
|
|
|
|
|
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);
|
2016-02-13 20:32:11 +01:00
|
|
|
needs_space = true;
|
|
|
|
|
2014-11-20 23:12:29 +01:00
|
|
|
} else if (type == SD_BUS_TYPE_VARIANT) {
|
|
|
|
|
|
|
|
if (needs_space)
|
|
|
|
fputc(' ', f);
|
|
|
|
|
|
|
|
fprintf(f, "%s", contents);
|
2016-02-13 20:32:11 +01:00
|
|
|
needs_space = true;
|
2014-11-20 23:12:29 +01:00
|
|
|
}
|
|
|
|
|
2016-02-13 20:32:11 +01:00
|
|
|
r = format_cmdline(m, f, needs_space);
|
2014-11-20 23:12:29 +01:00
|
|
|
if (r < 0)
|
|
|
|
return r;
|
|
|
|
|
2016-02-13 20:32:11 +01:00
|
|
|
needs_space = r > 0;
|
|
|
|
|
2014-11-20 23:12:29 +01:00
|
|
|
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.");
|
|
|
|
}
|
|
|
|
|
2014-12-02 12:58:13 +01:00
|
|
|
needs_space = true;
|
2014-11-20 23:12:29 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-11-19 21:12:54 +01:00
|
|
|
typedef struct Member {
|
|
|
|
const char *type;
|
|
|
|
char *interface;
|
|
|
|
char *name;
|
|
|
|
char *signature;
|
|
|
|
char *result;
|
2014-11-20 23:12:29 +01:00
|
|
|
char *value;
|
2014-11-19 21:12:54 +01:00
|
|
|
bool writable;
|
|
|
|
uint64_t flags;
|
|
|
|
} Member;
|
|
|
|
|
2015-10-04 00:22:41 +02:00
|
|
|
static void member_hash_func(const void *p, struct siphash *state) {
|
2014-11-19 21:12:54 +01:00
|
|
|
const Member *m = p;
|
2015-10-04 01:59:06 +02:00
|
|
|
uint64_t arity = 1;
|
2014-11-19 21:12:54 +01:00
|
|
|
|
|
|
|
assert(m);
|
|
|
|
assert(m->type);
|
|
|
|
|
2015-10-04 00:22:41 +02:00
|
|
|
string_hash_func(m->type, state);
|
2014-11-19 21:12:54 +01:00
|
|
|
|
2015-10-04 01:59:06 +02:00
|
|
|
arity += !!m->name + !!m->interface;
|
|
|
|
|
|
|
|
uint64_hash_func(&arity, state);
|
|
|
|
|
2014-11-19 21:12:54 +01:00
|
|
|
if (m->name)
|
2015-10-04 00:22:41 +02:00
|
|
|
string_hash_func(m->name, state);
|
2014-11-19 21:12:54 +01:00
|
|
|
|
|
|
|
if (m->interface)
|
2015-10-04 00:22:41 +02:00
|
|
|
string_hash_func(m->interface, state);
|
2014-11-19 21:12:54 +01:00
|
|
|
}
|
|
|
|
|
2018-09-19 01:28:50 +02:00
|
|
|
static int member_compare_func(const Member *x, const Member *y) {
|
2014-11-19 21:12:54 +01:00
|
|
|
int d;
|
|
|
|
|
|
|
|
assert(x);
|
|
|
|
assert(y);
|
|
|
|
assert(x->type);
|
|
|
|
assert(y->type);
|
|
|
|
|
2015-07-31 17:23:47 +02:00
|
|
|
d = strcmp_ptr(x->interface, y->interface);
|
|
|
|
if (d != 0)
|
|
|
|
return d;
|
2014-11-19 21:12:54 +01:00
|
|
|
|
|
|
|
d = strcmp(x->type, y->type);
|
|
|
|
if (d != 0)
|
|
|
|
return d;
|
|
|
|
|
2015-07-31 17:23:47 +02:00
|
|
|
return strcmp_ptr(x->name, y->name);
|
2014-11-19 21:12:54 +01:00
|
|
|
}
|
|
|
|
|
2018-09-18 01:39:24 +02:00
|
|
|
static int member_compare_funcp(Member * const *a, Member * const *b) {
|
|
|
|
return member_compare_func(*a, *b);
|
2014-11-19 21:12:54 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
static void member_free(Member *m) {
|
|
|
|
if (!m)
|
|
|
|
return;
|
|
|
|
|
|
|
|
free(m->interface);
|
|
|
|
free(m->name);
|
|
|
|
free(m->signature);
|
|
|
|
free(m->result);
|
2014-11-20 23:12:29 +01:00
|
|
|
free(m->value);
|
2014-11-19 21:12:54 +01:00
|
|
|
free(m);
|
|
|
|
}
|
|
|
|
|
|
|
|
DEFINE_TRIVIAL_CLEANUP_FUNC(Member*, member_free);
|
|
|
|
|
|
|
|
static void member_set_free(Set *s) {
|
2017-11-28 12:35:49 +01:00
|
|
|
set_free_with_destructor(s, member_free);
|
2014-11-19 21:12:54 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
DEFINE_TRIVIAL_CLEANUP_FUNC(Set*, member_set_free);
|
|
|
|
|
|
|
|
static int on_interface(const char *interface, uint64_t flags, void *userdata) {
|
|
|
|
_cleanup_(member_freep) Member *m;
|
|
|
|
Set *members = userdata;
|
|
|
|
int r;
|
|
|
|
|
|
|
|
assert(interface);
|
|
|
|
assert(members);
|
|
|
|
|
|
|
|
m = new0(Member, 1);
|
|
|
|
if (!m)
|
|
|
|
return log_oom();
|
|
|
|
|
|
|
|
m->type = "interface";
|
|
|
|
m->flags = flags;
|
|
|
|
|
|
|
|
r = free_and_strdup(&m->interface, interface);
|
|
|
|
if (r < 0)
|
|
|
|
return log_oom();
|
|
|
|
|
|
|
|
r = set_put(members, m);
|
|
|
|
if (r <= 0) {
|
|
|
|
log_error("Duplicate interface");
|
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
|
|
|
|
m = NULL;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int on_method(const char *interface, const char *name, const char *signature, const char *result, uint64_t flags, void *userdata) {
|
|
|
|
_cleanup_(member_freep) Member *m;
|
|
|
|
Set *members = userdata;
|
|
|
|
int r;
|
|
|
|
|
|
|
|
assert(interface);
|
|
|
|
assert(name);
|
|
|
|
|
|
|
|
m = new0(Member, 1);
|
|
|
|
if (!m)
|
|
|
|
return log_oom();
|
|
|
|
|
|
|
|
m->type = "method";
|
|
|
|
m->flags = flags;
|
|
|
|
|
|
|
|
r = free_and_strdup(&m->interface, interface);
|
|
|
|
if (r < 0)
|
|
|
|
return log_oom();
|
|
|
|
|
|
|
|
r = free_and_strdup(&m->name, name);
|
|
|
|
if (r < 0)
|
|
|
|
return log_oom();
|
|
|
|
|
|
|
|
r = free_and_strdup(&m->signature, signature);
|
|
|
|
if (r < 0)
|
|
|
|
return log_oom();
|
|
|
|
|
|
|
|
r = free_and_strdup(&m->result, result);
|
|
|
|
if (r < 0)
|
|
|
|
return log_oom();
|
|
|
|
|
|
|
|
r = set_put(members, m);
|
|
|
|
if (r <= 0) {
|
|
|
|
log_error("Duplicate method");
|
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
|
|
|
|
m = NULL;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int on_signal(const char *interface, const char *name, const char *signature, uint64_t flags, void *userdata) {
|
|
|
|
_cleanup_(member_freep) Member *m;
|
|
|
|
Set *members = userdata;
|
|
|
|
int r;
|
|
|
|
|
|
|
|
assert(interface);
|
|
|
|
assert(name);
|
|
|
|
|
|
|
|
m = new0(Member, 1);
|
|
|
|
if (!m)
|
|
|
|
return log_oom();
|
|
|
|
|
|
|
|
m->type = "signal";
|
|
|
|
m->flags = flags;
|
|
|
|
|
|
|
|
r = free_and_strdup(&m->interface, interface);
|
|
|
|
if (r < 0)
|
|
|
|
return log_oom();
|
|
|
|
|
|
|
|
r = free_and_strdup(&m->name, name);
|
|
|
|
if (r < 0)
|
|
|
|
return log_oom();
|
|
|
|
|
|
|
|
r = free_and_strdup(&m->signature, signature);
|
|
|
|
if (r < 0)
|
|
|
|
return log_oom();
|
|
|
|
|
|
|
|
r = set_put(members, m);
|
|
|
|
if (r <= 0) {
|
|
|
|
log_error("Duplicate signal");
|
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
|
|
|
|
m = NULL;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int on_property(const char *interface, const char *name, const char *signature, bool writable, uint64_t flags, void *userdata) {
|
|
|
|
_cleanup_(member_freep) Member *m;
|
|
|
|
Set *members = userdata;
|
|
|
|
int r;
|
|
|
|
|
|
|
|
assert(interface);
|
|
|
|
assert(name);
|
|
|
|
|
|
|
|
m = new0(Member, 1);
|
|
|
|
if (!m)
|
|
|
|
return log_oom();
|
|
|
|
|
|
|
|
m->type = "property";
|
|
|
|
m->flags = flags;
|
|
|
|
m->writable = writable;
|
|
|
|
|
|
|
|
r = free_and_strdup(&m->interface, interface);
|
|
|
|
if (r < 0)
|
|
|
|
return log_oom();
|
|
|
|
|
|
|
|
r = free_and_strdup(&m->name, name);
|
|
|
|
if (r < 0)
|
|
|
|
return log_oom();
|
|
|
|
|
|
|
|
r = free_and_strdup(&m->signature, signature);
|
|
|
|
if (r < 0)
|
|
|
|
return log_oom();
|
|
|
|
|
|
|
|
r = set_put(members, m);
|
|
|
|
if (r <= 0) {
|
|
|
|
log_error("Duplicate property");
|
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
|
|
|
|
m = NULL;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2018-03-13 21:09:16 +01:00
|
|
|
static int introspect(int argc, char **argv, void *userdata) {
|
2014-11-19 21:12:54 +01:00
|
|
|
static const struct hash_ops member_hash_ops = {
|
|
|
|
.hash = member_hash_func,
|
2018-09-19 01:28:50 +02:00
|
|
|
.compare = (__compar_fn_t) member_compare_func,
|
2014-11-19 21:12:54 +01:00
|
|
|
};
|
|
|
|
|
|
|
|
static const XMLIntrospectOps ops = {
|
|
|
|
.on_interface = on_interface,
|
|
|
|
.on_method = on_method,
|
|
|
|
.on_signal = on_signal,
|
|
|
|
.on_property = on_property,
|
|
|
|
};
|
|
|
|
|
2018-03-13 21:09:16 +01:00
|
|
|
_cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL;
|
2017-11-14 17:59:50 +01:00
|
|
|
_cleanup_(sd_bus_message_unrefp) sd_bus_message *reply_xml = NULL;
|
tree-wide: expose "p"-suffix unref calls in public APIs to make gcc cleanup easy
GLIB has recently started to officially support the gcc cleanup
attribute in its public API, hence let's do the same for our APIs.
With this patch we'll define an xyz_unrefp() call for each public
xyz_unref() call, to make it easy to use inside a
__attribute__((cleanup())) expression. Then, all code is ported over to
make use of this.
The new calls are also documented in the man pages, with examples how to
use them (well, I only added docs where the _unref() call itself already
had docs, and the examples, only cover sd_bus_unrefp() and
sd_event_unrefp()).
This also renames sd_lldp_free() to sd_lldp_unref(), since that's how we
tend to call our destructors these days.
Note that this defines no public macro that wraps gcc's attribute and
makes it easier to use. While I think it's our duty in the library to
make our stuff easy to use, I figure it's not our duty to make gcc's own
features easy to use on its own. Most likely, client code which wants to
make use of this should define its own:
#define _cleanup_(function) __attribute__((cleanup(function)))
Or similar, to make the gcc feature easier to use.
Making this logic public has the benefit that we can remove three header
files whose only purpose was to define these functions internally.
See #2008.
2015-11-27 19:13:45 +01:00
|
|
|
_cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
|
2014-11-19 21:12:54 +01:00
|
|
|
_cleanup_(member_set_freep) Set *members = NULL;
|
2018-03-13 21:09:16 +01:00
|
|
|
unsigned name_width, type_width, signature_width, result_width, j, k = 0;
|
|
|
|
Member *m, **sorted = NULL;
|
2014-11-19 21:12:54 +01:00
|
|
|
Iterator i;
|
|
|
|
const char *xml;
|
|
|
|
int r;
|
|
|
|
|
2018-03-13 21:09:16 +01:00
|
|
|
r = acquire_bus(false, &bus);
|
|
|
|
if (r < 0)
|
|
|
|
return r;
|
2014-12-23 22:42:55 +01:00
|
|
|
|
2014-11-19 21:12:54 +01:00
|
|
|
members = set_new(&member_hash_ops);
|
|
|
|
if (!members)
|
|
|
|
return log_oom();
|
|
|
|
|
2017-11-14 17:59:50 +01:00
|
|
|
r = sd_bus_call_method(bus, argv[1], argv[2], "org.freedesktop.DBus.Introspectable", "Introspect", &error, &reply_xml, "");
|
2017-12-11 20:31:36 +01:00
|
|
|
if (r < 0)
|
|
|
|
return log_error_errno(r, "Failed to introspect object %s of service %s: %s", argv[2], argv[1], bus_error_message(&error, r));
|
2014-11-19 21:12:54 +01:00
|
|
|
|
2017-11-14 17:59:50 +01:00
|
|
|
r = sd_bus_message_read(reply_xml, "s", &xml);
|
2014-11-19 21:12:54 +01:00
|
|
|
if (r < 0)
|
|
|
|
return bus_log_parse_error(r);
|
|
|
|
|
2014-11-20 23:12:29 +01:00
|
|
|
/* First, get list of all properties */
|
2014-11-19 21:12:54 +01:00
|
|
|
r = parse_xml_introspect(argv[2], xml, &ops, members);
|
|
|
|
if (r < 0)
|
|
|
|
return r;
|
|
|
|
|
2014-11-20 23:12:29 +01:00
|
|
|
/* Second, find the current values for them */
|
|
|
|
SET_FOREACH(m, members, i) {
|
2017-11-14 17:59:50 +01:00
|
|
|
_cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
|
2014-11-20 23:12:29 +01:00
|
|
|
|
|
|
|
if (!streq(m->type, "property"))
|
|
|
|
continue;
|
|
|
|
|
|
|
|
if (m->value)
|
|
|
|
continue;
|
|
|
|
|
2014-12-23 22:42:55 +01:00
|
|
|
if (argv[3] && !streq(argv[3], m->interface))
|
|
|
|
continue;
|
|
|
|
|
2014-11-20 23:12:29 +01:00
|
|
|
r = sd_bus_call_method(bus, argv[1], argv[2], "org.freedesktop.DBus.Properties", "GetAll", &error, &reply, "s", m->interface);
|
2017-12-11 20:31:36 +01:00
|
|
|
if (r < 0)
|
|
|
|
return log_error_errno(r, "%s", bus_error_message(&error, r));
|
2014-11-20 23:12:29 +01:00
|
|
|
|
|
|
|
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();
|
|
|
|
|
2017-12-11 19:50:30 +01:00
|
|
|
(void) __fsetlocking(mf, FSETLOCKING_BYCALLER);
|
|
|
|
|
2014-11-20 23:12:29 +01:00
|
|
|
r = format_cmdline(reply, mf, false);
|
|
|
|
if (r < 0)
|
|
|
|
return bus_log_parse_error(r);
|
|
|
|
|
2017-12-11 20:30:07 +01:00
|
|
|
mf = safe_fclose(mf);
|
2014-11-20 23:12:29 +01:00
|
|
|
|
|
|
|
z = set_get(members, &((Member) {
|
|
|
|
.type = "property",
|
|
|
|
.interface = m->interface,
|
|
|
|
.name = (char*) name }));
|
2017-12-11 20:30:07 +01:00
|
|
|
if (z)
|
|
|
|
free_and_replace(z->value, buf);
|
2014-11-20 23:12:29 +01:00
|
|
|
|
|
|
|
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);
|
|
|
|
}
|
|
|
|
|
2018-03-14 09:48:29 +01:00
|
|
|
(void) pager_open(arg_no_pager, false);
|
2014-11-19 21:12:54 +01:00
|
|
|
|
2017-12-14 19:02:29 +01:00
|
|
|
name_width = STRLEN("NAME");
|
|
|
|
type_width = STRLEN("TYPE");
|
|
|
|
signature_width = STRLEN("SIGNATURE");
|
|
|
|
result_width = STRLEN("RESULT/VALUE");
|
2014-11-19 21:12:54 +01:00
|
|
|
|
|
|
|
sorted = newa(Member*, set_size(members));
|
|
|
|
|
|
|
|
SET_FOREACH(m, members, i) {
|
2014-12-23 22:42:55 +01:00
|
|
|
|
|
|
|
if (argv[3] && !streq(argv[3], m->interface))
|
|
|
|
continue;
|
|
|
|
|
2014-11-19 21:12:54 +01:00
|
|
|
if (m->interface)
|
|
|
|
name_width = MAX(name_width, strlen(m->interface));
|
|
|
|
if (m->name)
|
|
|
|
name_width = MAX(name_width, strlen(m->name) + 1);
|
|
|
|
if (m->type)
|
|
|
|
type_width = MAX(type_width, strlen(m->type));
|
|
|
|
if (m->signature)
|
|
|
|
signature_width = MAX(signature_width, strlen(m->signature));
|
|
|
|
if (m->result)
|
|
|
|
result_width = MAX(result_width, strlen(m->result));
|
2014-11-20 23:12:29 +01:00
|
|
|
if (m->value)
|
|
|
|
result_width = MAX(result_width, strlen(m->value));
|
2014-11-19 21:12:54 +01:00
|
|
|
|
|
|
|
sorted[k++] = m;
|
|
|
|
}
|
|
|
|
|
2014-11-20 23:12:29 +01:00
|
|
|
if (result_width > 40)
|
|
|
|
result_width = 40;
|
|
|
|
|
2018-09-18 01:39:24 +02:00
|
|
|
typesafe_qsort(sorted, k, member_compare_funcp);
|
2014-11-19 21:12:54 +01:00
|
|
|
|
2014-11-20 23:12:29 +01:00
|
|
|
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/VALUE",
|
|
|
|
"FLAGS");
|
|
|
|
}
|
2014-11-19 21:12:54 +01:00
|
|
|
|
|
|
|
for (j = 0; j < k; j++) {
|
2014-11-20 23:12:29 +01:00
|
|
|
_cleanup_free_ char *ellipsized = NULL;
|
|
|
|
const char *rv;
|
2014-11-19 21:12:54 +01:00
|
|
|
bool is_interface;
|
|
|
|
|
|
|
|
m = sorted[j];
|
|
|
|
|
2014-12-23 22:42:55 +01:00
|
|
|
if (argv[3] && !streq(argv[3], m->interface))
|
|
|
|
continue;
|
|
|
|
|
2014-11-19 21:12:54 +01:00
|
|
|
is_interface = streq(m->type, "interface");
|
|
|
|
|
2014-12-23 22:42:55 +01:00
|
|
|
if (argv[3] && is_interface)
|
|
|
|
continue;
|
|
|
|
|
2014-11-20 23:12:29 +01:00
|
|
|
if (m->value) {
|
|
|
|
ellipsized = ellipsize(m->value, result_width, 100);
|
|
|
|
if (!ellipsized)
|
|
|
|
return log_oom();
|
|
|
|
|
|
|
|
rv = ellipsized;
|
|
|
|
} else
|
2018-05-10 18:55:46 +02:00
|
|
|
rv = empty_to_dash(m->result);
|
2014-11-20 23:12:29 +01:00
|
|
|
|
2014-11-19 21:12:54 +01:00
|
|
|
printf("%s%s%-*s%s %-*s %-*s %-*s%s%s%s%s%s%s\n",
|
|
|
|
is_interface ? ansi_highlight() : "",
|
|
|
|
is_interface ? "" : ".",
|
2018-05-10 18:55:46 +02:00
|
|
|
- !is_interface + (int) name_width, empty_to_dash(streq_ptr(m->type, "interface") ? m->interface : m->name),
|
2015-09-19 00:45:05 +02:00
|
|
|
is_interface ? ansi_normal() : "",
|
2018-05-10 18:55:46 +02:00
|
|
|
(int) type_width, empty_to_dash(m->type),
|
|
|
|
(int) signature_width, empty_to_dash(m->signature),
|
2014-11-20 23:12:29 +01:00
|
|
|
(int) result_width, rv,
|
2014-11-19 21:12:54 +01:00
|
|
|
(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" : "",
|
|
|
|
(m->flags & SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE) ? " emits-change" : "",
|
|
|
|
(m->flags & SD_BUS_VTABLE_PROPERTY_EMITS_INVALIDATION) ? " emits-invalidation" : "",
|
|
|
|
m->writable ? " writable" : "");
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2014-10-30 01:13:11 +01:00
|
|
|
static int message_dump(sd_bus_message *m, FILE *f) {
|
2014-11-14 17:20:04 +01:00
|
|
|
return bus_message_dump(m, f, BUS_MESSAGE_DUMP_WITH_HEADER);
|
2014-10-30 01:13:11 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
static int message_pcap(sd_bus_message *m, FILE *f) {
|
|
|
|
return bus_message_pcap_frame(m, arg_snaplen, f);
|
|
|
|
}
|
|
|
|
|
2018-03-13 21:09:16 +01:00
|
|
|
static int monitor(int argc, char **argv, int (*dump)(sd_bus_message *m, FILE *f)) {
|
|
|
|
_cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL;
|
2016-05-14 22:10:22 +02:00
|
|
|
_cleanup_(sd_bus_message_unrefp) sd_bus_message *message = NULL;
|
|
|
|
_cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
|
2013-04-24 17:56:28 +02:00
|
|
|
char **i;
|
2016-05-14 22:10:22 +02:00
|
|
|
uint32_t flags = 0;
|
2017-05-11 15:56:55 +02:00
|
|
|
const char *unique_name;
|
|
|
|
bool is_monitor = false;
|
2013-04-24 17:56:28 +02:00
|
|
|
int r;
|
|
|
|
|
2018-03-13 21:09:16 +01:00
|
|
|
r = acquire_bus(true, &bus);
|
|
|
|
if (r < 0)
|
|
|
|
return r;
|
|
|
|
|
2016-05-14 22:10:22 +02:00
|
|
|
/* upgrade connection; it's not used for anything else after this call */
|
|
|
|
r = sd_bus_message_new_method_call(bus, &message, "org.freedesktop.DBus", "/org/freedesktop/DBus", "org.freedesktop.DBus.Monitoring", "BecomeMonitor");
|
|
|
|
if (r < 0)
|
|
|
|
return bus_log_create_error(r);
|
|
|
|
|
|
|
|
r = sd_bus_message_open_container(message, 'a', "s");
|
|
|
|
if (r < 0)
|
|
|
|
return bus_log_create_error(r);
|
|
|
|
|
2013-04-24 17:56:28 +02:00
|
|
|
STRV_FOREACH(i, argv+1) {
|
|
|
|
_cleanup_free_ char *m = NULL;
|
|
|
|
|
|
|
|
if (!service_name_is_valid(*i)) {
|
|
|
|
log_error("Invalid service name '%s'", *i);
|
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
|
2016-10-23 17:43:27 +02:00
|
|
|
m = strjoin("sender='", *i, "'");
|
2013-04-24 17:56:28 +02:00
|
|
|
if (!m)
|
|
|
|
return log_oom();
|
|
|
|
|
2016-05-14 22:10:22 +02:00
|
|
|
r = sd_bus_message_append_basic(message, 's', m);
|
2014-11-28 18:23:20 +01:00
|
|
|
if (r < 0)
|
2016-05-14 22:10:22 +02:00
|
|
|
return bus_log_create_error(r);
|
2013-11-19 21:37:32 +01:00
|
|
|
|
2015-09-23 00:06:18 +02:00
|
|
|
free(m);
|
2016-10-23 17:43:27 +02:00
|
|
|
m = strjoin("destination='", *i, "'");
|
2015-09-23 00:06:18 +02:00
|
|
|
if (!m)
|
|
|
|
return log_oom();
|
|
|
|
|
2016-05-14 22:10:22 +02:00
|
|
|
r = sd_bus_message_append_basic(message, 's', m);
|
2015-09-23 00:06:18 +02:00
|
|
|
if (r < 0)
|
2016-05-14 22:10:22 +02:00
|
|
|
return bus_log_create_error(r);
|
2013-04-24 17:56:28 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
STRV_FOREACH(i, arg_matches) {
|
2016-05-14 22:10:22 +02:00
|
|
|
r = sd_bus_message_append_basic(message, 's', *i);
|
2014-11-28 18:23:20 +01:00
|
|
|
if (r < 0)
|
2016-05-14 22:10:22 +02:00
|
|
|
return bus_log_create_error(r);
|
2013-11-19 21:37:32 +01:00
|
|
|
}
|
|
|
|
|
2016-05-14 22:10:22 +02:00
|
|
|
r = sd_bus_message_close_container(message);
|
|
|
|
if (r < 0)
|
|
|
|
return bus_log_create_error(r);
|
|
|
|
|
|
|
|
r = sd_bus_message_append_basic(message, 'u', &flags);
|
|
|
|
if (r < 0)
|
|
|
|
return bus_log_create_error(r);
|
|
|
|
|
|
|
|
r = sd_bus_call(bus, message, arg_timeout, &error, NULL);
|
2018-06-14 14:54:32 +02:00
|
|
|
if (r < 0)
|
|
|
|
return log_error_errno(r, "%s", bus_error_message(&error, r));
|
2013-04-24 17:56:28 +02:00
|
|
|
|
2017-05-11 15:56:55 +02:00
|
|
|
r = sd_bus_get_unique_name(bus, &unique_name);
|
|
|
|
if (r < 0)
|
|
|
|
return log_error_errno(r, "Failed to get unique name: %m");
|
|
|
|
|
2015-01-07 20:42:14 +01:00
|
|
|
log_info("Monitoring bus message stream.");
|
|
|
|
|
2013-04-24 17:56:28 +02:00
|
|
|
for (;;) {
|
tree-wide: expose "p"-suffix unref calls in public APIs to make gcc cleanup easy
GLIB has recently started to officially support the gcc cleanup
attribute in its public API, hence let's do the same for our APIs.
With this patch we'll define an xyz_unrefp() call for each public
xyz_unref() call, to make it easy to use inside a
__attribute__((cleanup())) expression. Then, all code is ported over to
make use of this.
The new calls are also documented in the man pages, with examples how to
use them (well, I only added docs where the _unref() call itself already
had docs, and the examples, only cover sd_bus_unrefp() and
sd_event_unrefp()).
This also renames sd_lldp_free() to sd_lldp_unref(), since that's how we
tend to call our destructors these days.
Note that this defines no public macro that wraps gcc's attribute and
makes it easier to use. While I think it's our duty in the library to
make our stuff easy to use, I figure it's not our duty to make gcc's own
features easy to use on its own. Most likely, client code which wants to
make use of this should define its own:
#define _cleanup_(function) __attribute__((cleanup(function)))
Or similar, to make the gcc feature easier to use.
Making this logic public has the benefit that we can remove three header
files whose only purpose was to define these functions internally.
See #2008.
2015-11-27 19:13:45 +01:00
|
|
|
_cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL;
|
2013-04-24 17:56:28 +02:00
|
|
|
|
|
|
|
r = sd_bus_process(bus, &m);
|
2014-11-28 18:23:20 +01:00
|
|
|
if (r < 0)
|
|
|
|
return log_error_errno(r, "Failed to process bus: %m");
|
2013-04-24 17:56:28 +02:00
|
|
|
|
2017-05-11 15:56:55 +02:00
|
|
|
if (!is_monitor) {
|
|
|
|
const char *name;
|
|
|
|
|
|
|
|
/* wait until we lose our unique name */
|
|
|
|
if (sd_bus_message_is_signal(m, "org.freedesktop.DBus", "NameLost") <= 0)
|
|
|
|
continue;
|
|
|
|
|
|
|
|
r = sd_bus_message_read(m, "s", &name);
|
|
|
|
if (r < 0)
|
|
|
|
return log_error_errno(r, "Failed to read lost name: %m");
|
|
|
|
|
2017-05-25 16:10:37 +02:00
|
|
|
if (streq(name, unique_name))
|
|
|
|
is_monitor = true;
|
2017-05-11 15:56:55 +02:00
|
|
|
|
2017-05-25 16:10:37 +02:00
|
|
|
continue;
|
2017-05-11 15:56:55 +02:00
|
|
|
}
|
|
|
|
|
2013-04-24 17:56:28 +02:00
|
|
|
if (m) {
|
2014-10-30 01:13:11 +01:00
|
|
|
dump(m, stdout);
|
2015-07-04 12:11:22 +02:00
|
|
|
fflush(stdout);
|
2015-01-07 20:42:14 +01:00
|
|
|
|
|
|
|
if (sd_bus_message_is_signal(m, "org.freedesktop.DBus.Local", "Disconnected") > 0) {
|
|
|
|
log_info("Connection terminated, exiting.");
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2013-04-24 17:56:28 +02:00
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (r > 0)
|
|
|
|
continue;
|
|
|
|
|
|
|
|
r = sd_bus_wait(bus, (uint64_t) -1);
|
2014-11-28 18:23:20 +01:00
|
|
|
if (r < 0)
|
|
|
|
return log_error_errno(r, "Failed to wait for bus: %m");
|
2013-04-24 17:56:28 +02:00
|
|
|
}
|
2013-12-02 18:40:19 +01:00
|
|
|
}
|
2013-04-24 17:56:28 +02:00
|
|
|
|
2018-03-13 21:09:16 +01:00
|
|
|
static int verb_monitor(int argc, char **argv, void *userdata) {
|
|
|
|
return monitor(argc, argv, message_dump);
|
|
|
|
}
|
|
|
|
|
|
|
|
static int verb_capture(int argc, char **argv, void *userdata) {
|
2014-10-30 01:13:11 +01:00
|
|
|
int r;
|
|
|
|
|
|
|
|
if (isatty(fileno(stdout)) > 0) {
|
|
|
|
log_error("Refusing to write message data to console, please redirect output to a file.");
|
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
|
|
|
|
bus_pcap_header(arg_snaplen, stdout);
|
|
|
|
|
2018-03-13 21:09:16 +01:00
|
|
|
r = monitor(argc, argv, message_pcap);
|
2014-10-30 01:13:11 +01:00
|
|
|
if (r < 0)
|
|
|
|
return r;
|
|
|
|
|
2018-06-14 14:53:46 +02:00
|
|
|
r = fflush_and_check(stdout);
|
|
|
|
if (r < 0)
|
|
|
|
return log_error_errno(r, "Couldn't write capture file: %m");
|
2014-10-30 01:13:11 +01:00
|
|
|
|
|
|
|
return r;
|
|
|
|
}
|
|
|
|
|
2018-03-13 21:09:16 +01:00
|
|
|
static int status(int argc, char **argv, void *userdata) {
|
|
|
|
_cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL;
|
tree-wide: expose "p"-suffix unref calls in public APIs to make gcc cleanup easy
GLIB has recently started to officially support the gcc cleanup
attribute in its public API, hence let's do the same for our APIs.
With this patch we'll define an xyz_unrefp() call for each public
xyz_unref() call, to make it easy to use inside a
__attribute__((cleanup())) expression. Then, all code is ported over to
make use of this.
The new calls are also documented in the man pages, with examples how to
use them (well, I only added docs where the _unref() call itself already
had docs, and the examples, only cover sd_bus_unrefp() and
sd_event_unrefp()).
This also renames sd_lldp_free() to sd_lldp_unref(), since that's how we
tend to call our destructors these days.
Note that this defines no public macro that wraps gcc's attribute and
makes it easier to use. While I think it's our duty in the library to
make our stuff easy to use, I figure it's not our duty to make gcc's own
features easy to use on its own. Most likely, client code which wants to
make use of this should define its own:
#define _cleanup_(function) __attribute__((cleanup(function)))
Or similar, to make the gcc feature easier to use.
Making this logic public has the benefit that we can remove three header
files whose only purpose was to define these functions internally.
See #2008.
2015-11-27 19:13:45 +01:00
|
|
|
_cleanup_(sd_bus_creds_unrefp) sd_bus_creds *creds = NULL;
|
2013-12-02 18:40:19 +01:00
|
|
|
pid_t pid;
|
|
|
|
int r;
|
|
|
|
|
2018-03-13 21:09:16 +01:00
|
|
|
r = acquire_bus(false, &bus);
|
|
|
|
if (r < 0)
|
|
|
|
return r;
|
2013-12-02 18:40:19 +01:00
|
|
|
|
2018-03-13 21:09:16 +01:00
|
|
|
if (!isempty(argv[1])) {
|
2014-11-25 02:11:57 +01:00
|
|
|
r = parse_pid(argv[1], &pid);
|
|
|
|
if (r < 0)
|
|
|
|
r = sd_bus_get_name_creds(
|
|
|
|
bus,
|
|
|
|
argv[1],
|
|
|
|
(arg_augment_creds ? SD_BUS_CREDS_AUGMENT : 0) | _SD_BUS_CREDS_ALL,
|
|
|
|
&creds);
|
|
|
|
else
|
|
|
|
r = sd_bus_creds_new_from_pid(
|
|
|
|
&creds,
|
|
|
|
pid,
|
|
|
|
_SD_BUS_CREDS_ALL);
|
2014-11-28 15:59:05 +01:00
|
|
|
} else {
|
2014-11-28 16:51:45 +01:00
|
|
|
const char *scope, *address;
|
2014-11-28 16:38:47 +01:00
|
|
|
sd_id128_t bus_id;
|
2014-11-28 15:59:05 +01:00
|
|
|
|
2014-11-28 16:51:45 +01:00
|
|
|
r = sd_bus_get_address(bus, &address);
|
|
|
|
if (r >= 0)
|
2015-09-19 00:45:05 +02:00
|
|
|
printf("BusAddress=%s%s%s\n", ansi_highlight(), address, ansi_normal());
|
2014-11-28 16:51:45 +01:00
|
|
|
|
2014-11-28 15:59:05 +01:00
|
|
|
r = sd_bus_get_scope(bus, &scope);
|
|
|
|
if (r >= 0)
|
2015-09-19 00:45:05 +02:00
|
|
|
printf("BusScope=%s%s%s\n", ansi_highlight(), scope, ansi_normal());
|
2014-11-28 15:59:05 +01:00
|
|
|
|
2014-11-28 16:38:47 +01:00
|
|
|
r = sd_bus_get_bus_id(bus, &bus_id);
|
|
|
|
if (r >= 0)
|
2015-09-19 00:45:05 +02:00
|
|
|
printf("BusID=%s" SD_ID128_FORMAT_STR "%s\n", ansi_highlight(), SD_ID128_FORMAT_VAL(bus_id), ansi_normal());
|
2014-11-28 16:38:47 +01:00
|
|
|
|
2014-11-25 02:11:57 +01:00
|
|
|
r = sd_bus_get_owner_creds(
|
2014-11-24 21:53:29 +01:00
|
|
|
bus,
|
|
|
|
(arg_augment_creds ? SD_BUS_CREDS_AUGMENT : 0) | _SD_BUS_CREDS_ALL,
|
|
|
|
&creds);
|
2014-11-28 15:59:05 +01:00
|
|
|
}
|
2013-12-02 18:40:19 +01:00
|
|
|
|
2014-11-28 18:23:20 +01:00
|
|
|
if (r < 0)
|
|
|
|
return log_error_errno(r, "Failed to get credentials: %m");
|
2013-12-02 18:40:19 +01:00
|
|
|
|
2014-11-19 23:59:26 +01:00
|
|
|
bus_creds_dump(creds, NULL, false);
|
2013-12-02 18:40:19 +01:00
|
|
|
return 0;
|
2013-04-24 17:56:28 +02:00
|
|
|
}
|
|
|
|
|
2014-11-14 13:11:10 +01:00
|
|
|
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);
|
2018-06-14 14:54:32 +02:00
|
|
|
if (r < 0)
|
|
|
|
return log_error_errno(r, "Failed to parse as boolean: %s", v);
|
2014-11-14 13:11:10 +01:00
|
|
|
|
|
|
|
r = sd_bus_message_append_basic(m, t, &r);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case SD_BUS_TYPE_BYTE: {
|
|
|
|
uint8_t z;
|
|
|
|
|
|
|
|
r = safe_atou8(v, &z);
|
2018-06-14 14:54:32 +02:00
|
|
|
if (r < 0)
|
|
|
|
return log_error_errno(r, "Failed to parse as byte (unsigned 8bit integer): %s", v);
|
2014-11-14 13:11:10 +01:00
|
|
|
|
|
|
|
r = sd_bus_message_append_basic(m, t, &z);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
case SD_BUS_TYPE_INT16: {
|
|
|
|
int16_t z;
|
|
|
|
|
|
|
|
r = safe_atoi16(v, &z);
|
2018-06-14 14:54:32 +02:00
|
|
|
if (r < 0)
|
|
|
|
return log_error_errno(r, "Failed to parse as signed 16bit integer: %s", v);
|
2014-11-14 13:11:10 +01:00
|
|
|
|
|
|
|
r = sd_bus_message_append_basic(m, t, &z);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
case SD_BUS_TYPE_UINT16: {
|
|
|
|
uint16_t z;
|
|
|
|
|
|
|
|
r = safe_atou16(v, &z);
|
2018-06-14 14:54:32 +02:00
|
|
|
if (r < 0)
|
|
|
|
return log_error_errno(r, "Failed to parse as unsigned 16bit integer: %s", v);
|
2014-11-14 13:11:10 +01:00
|
|
|
|
|
|
|
r = sd_bus_message_append_basic(m, t, &z);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
case SD_BUS_TYPE_INT32: {
|
|
|
|
int32_t z;
|
|
|
|
|
|
|
|
r = safe_atoi32(v, &z);
|
2018-06-14 14:54:32 +02:00
|
|
|
if (r < 0)
|
|
|
|
return log_error_errno(r, "Failed to parse as signed 32bit integer: %s", v);
|
2014-11-14 13:11:10 +01:00
|
|
|
|
|
|
|
r = sd_bus_message_append_basic(m, t, &z);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
case SD_BUS_TYPE_UINT32: {
|
|
|
|
uint32_t z;
|
|
|
|
|
|
|
|
r = safe_atou32(v, &z);
|
2018-06-14 14:54:32 +02:00
|
|
|
if (r < 0)
|
|
|
|
return log_error_errno(r, "Failed to parse as unsigned 32bit integer: %s", v);
|
2014-11-14 13:11:10 +01:00
|
|
|
|
|
|
|
r = sd_bus_message_append_basic(m, t, &z);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
case SD_BUS_TYPE_INT64: {
|
|
|
|
int64_t z;
|
|
|
|
|
|
|
|
r = safe_atoi64(v, &z);
|
2018-06-14 14:54:32 +02:00
|
|
|
if (r < 0)
|
|
|
|
return log_error_errno(r, "Failed to parse as signed 64bit integer: %s", v);
|
2014-11-14 13:11:10 +01:00
|
|
|
|
|
|
|
r = sd_bus_message_append_basic(m, t, &z);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
case SD_BUS_TYPE_UINT64: {
|
|
|
|
uint64_t z;
|
|
|
|
|
|
|
|
r = safe_atou64(v, &z);
|
2018-06-14 14:54:32 +02:00
|
|
|
if (r < 0)
|
|
|
|
return log_error_errno(r, "Failed to parse as unsigned 64bit integer: %s", v);
|
2014-11-14 13:11:10 +01:00
|
|
|
|
|
|
|
r = sd_bus_message_append_basic(m, t, &z);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
case SD_BUS_TYPE_DOUBLE: {
|
|
|
|
double z;
|
|
|
|
|
|
|
|
r = safe_atod(v, &z);
|
2018-06-14 14:54:32 +02:00
|
|
|
if (r < 0)
|
|
|
|
return log_error_errno(r, "Failed to parse as double precision floating point: %s", v);
|
2014-11-14 13:11:10 +01:00
|
|
|
|
|
|
|
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);
|
2018-06-14 14:54:32 +02:00
|
|
|
if (r < 0)
|
|
|
|
return log_error_errno(r, "Failed to parse number of array entries: %s", v);
|
2014-11-14 13:11:10 +01:00
|
|
|
|
|
|
|
r = signature_element_length(signature, &k);
|
2018-06-14 14:54:32 +02:00
|
|
|
if (r < 0)
|
|
|
|
return log_error_errno(r, "Invalid array signature.");
|
2014-11-14 13:11:10 +01:00
|
|
|
|
|
|
|
{
|
|
|
|
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);
|
2018-06-14 14:54:32 +02:00
|
|
|
if (r < 0)
|
|
|
|
return log_error_errno(r, "Invalid struct/dict entry signature.");
|
2014-11-14 13:11:10 +01:00
|
|
|
|
|
|
|
{
|
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2018-07-04 15:28:09 +02:00
|
|
|
static int json_transform_one(sd_bus_message *m, JsonVariant **ret);
|
|
|
|
|
|
|
|
static int json_transform_array_or_struct(sd_bus_message *m, JsonVariant **ret) {
|
|
|
|
size_t n_elements = 0, n_allocated = 0;
|
|
|
|
JsonVariant **elements = NULL;
|
|
|
|
int r;
|
|
|
|
|
|
|
|
assert(m);
|
|
|
|
assert(ret);
|
|
|
|
|
|
|
|
for (;;) {
|
|
|
|
r = sd_bus_message_at_end(m, false);
|
|
|
|
if (r < 0) {
|
|
|
|
bus_log_parse_error(r);
|
|
|
|
goto finish;
|
|
|
|
}
|
|
|
|
if (r > 0)
|
|
|
|
break;
|
|
|
|
|
|
|
|
if (!GREEDY_REALLOC(elements, n_allocated, n_elements + 1)) {
|
|
|
|
r = log_oom();
|
|
|
|
goto finish;
|
|
|
|
}
|
|
|
|
|
|
|
|
r = json_transform_one(m, elements + n_elements);
|
|
|
|
if (r < 0)
|
|
|
|
goto finish;
|
|
|
|
|
|
|
|
n_elements++;
|
|
|
|
}
|
|
|
|
|
|
|
|
r = json_variant_new_array(ret, elements, n_elements);
|
|
|
|
|
|
|
|
finish:
|
|
|
|
json_variant_unref_many(elements, n_elements);
|
|
|
|
free(elements);
|
|
|
|
|
|
|
|
return r;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int json_transform_variant(sd_bus_message *m, const char *contents, JsonVariant **ret) {
|
|
|
|
_cleanup_(json_variant_unrefp) JsonVariant *type = NULL, *value = NULL;
|
|
|
|
int r;
|
|
|
|
|
|
|
|
assert(m);
|
|
|
|
assert(contents);
|
|
|
|
assert(ret);
|
|
|
|
|
|
|
|
r = json_transform_one(m, &value);
|
|
|
|
if (r < 0)
|
|
|
|
return r;
|
|
|
|
|
|
|
|
r = json_build(ret, JSON_BUILD_OBJECT(JSON_BUILD_PAIR("type", JSON_BUILD_STRING(contents)),
|
|
|
|
JSON_BUILD_PAIR("data", JSON_BUILD_VARIANT(value))));
|
|
|
|
if (r < 0)
|
|
|
|
return log_oom();
|
|
|
|
|
|
|
|
return r;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int json_transform_dict_array(sd_bus_message *m, JsonVariant **ret) {
|
|
|
|
size_t n_elements = 0, n_allocated = 0;
|
|
|
|
JsonVariant **elements = NULL;
|
|
|
|
int r;
|
|
|
|
|
|
|
|
assert(m);
|
|
|
|
assert(ret);
|
|
|
|
|
|
|
|
for (;;) {
|
|
|
|
const char *contents;
|
|
|
|
char type;
|
|
|
|
|
|
|
|
r = sd_bus_message_at_end(m, false);
|
|
|
|
if (r < 0) {
|
|
|
|
bus_log_parse_error(r);
|
|
|
|
goto finish;
|
|
|
|
}
|
|
|
|
if (r > 0)
|
|
|
|
break;
|
|
|
|
|
|
|
|
r = sd_bus_message_peek_type(m, &type, &contents);
|
|
|
|
if (r < 0)
|
|
|
|
return r;
|
|
|
|
|
|
|
|
assert(type == 'e');
|
|
|
|
|
|
|
|
if (!GREEDY_REALLOC(elements, n_allocated, n_elements + 2)) {
|
|
|
|
r = log_oom();
|
|
|
|
goto finish;
|
|
|
|
}
|
|
|
|
|
|
|
|
r = sd_bus_message_enter_container(m, type, contents);
|
|
|
|
if (r < 0) {
|
|
|
|
bus_log_parse_error(r);
|
|
|
|
goto finish;
|
|
|
|
}
|
|
|
|
|
|
|
|
r = json_transform_one(m, elements + n_elements);
|
|
|
|
if (r < 0)
|
|
|
|
goto finish;
|
|
|
|
|
|
|
|
n_elements++;
|
|
|
|
|
|
|
|
r = json_transform_one(m, elements + n_elements);
|
|
|
|
if (r < 0)
|
|
|
|
goto finish;
|
|
|
|
|
|
|
|
n_elements++;
|
|
|
|
|
|
|
|
r = sd_bus_message_exit_container(m);
|
|
|
|
if (r < 0) {
|
|
|
|
bus_log_parse_error(r);
|
|
|
|
goto finish;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
r = json_variant_new_object(ret, elements, n_elements);
|
|
|
|
|
|
|
|
finish:
|
|
|
|
json_variant_unref_many(elements, n_elements);
|
|
|
|
free(elements);
|
|
|
|
|
|
|
|
return r;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int json_transform_one(sd_bus_message *m, JsonVariant **ret) {
|
|
|
|
_cleanup_(json_variant_unrefp) JsonVariant *v = NULL;
|
|
|
|
const char *contents;
|
|
|
|
char type;
|
|
|
|
int r;
|
|
|
|
|
|
|
|
assert(m);
|
|
|
|
assert(ret);
|
|
|
|
|
|
|
|
r = sd_bus_message_peek_type(m, &type, &contents);
|
|
|
|
if (r < 0)
|
|
|
|
return bus_log_parse_error(r);
|
|
|
|
|
|
|
|
switch (type) {
|
|
|
|
|
|
|
|
case SD_BUS_TYPE_BYTE: {
|
|
|
|
uint8_t b;
|
|
|
|
|
|
|
|
r = sd_bus_message_read_basic(m, type, &b);
|
|
|
|
if (r < 0)
|
|
|
|
return bus_log_parse_error(r);
|
|
|
|
|
|
|
|
r = json_variant_new_unsigned(&v, b);
|
|
|
|
if (r < 0)
|
|
|
|
return log_error_errno(r, "Failed to transform byte: %m");
|
|
|
|
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
case SD_BUS_TYPE_BOOLEAN: {
|
|
|
|
int b;
|
|
|
|
|
|
|
|
r = sd_bus_message_read_basic(m, type, &b);
|
|
|
|
if (r < 0)
|
|
|
|
return bus_log_parse_error(r);
|
|
|
|
|
|
|
|
r = json_variant_new_boolean(&v, b);
|
|
|
|
if (r < 0)
|
|
|
|
return log_error_errno(r, "Failed to transform boolean: %m");
|
|
|
|
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
case SD_BUS_TYPE_INT16: {
|
|
|
|
int16_t b;
|
|
|
|
|
|
|
|
r = sd_bus_message_read_basic(m, type, &b);
|
|
|
|
if (r < 0)
|
|
|
|
return bus_log_parse_error(r);
|
|
|
|
|
|
|
|
r = json_variant_new_integer(&v, b);
|
|
|
|
if (r < 0)
|
|
|
|
return log_error_errno(r, "Failed to transform int16: %m");
|
|
|
|
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
case SD_BUS_TYPE_UINT16: {
|
|
|
|
uint16_t b;
|
|
|
|
|
|
|
|
r = sd_bus_message_read_basic(m, type, &b);
|
|
|
|
if (r < 0)
|
|
|
|
return bus_log_parse_error(r);
|
|
|
|
|
|
|
|
r = json_variant_new_unsigned(&v, b);
|
|
|
|
if (r < 0)
|
|
|
|
return log_error_errno(r, "Failed to transform uint16: %m");
|
|
|
|
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
case SD_BUS_TYPE_INT32: {
|
|
|
|
int32_t b;
|
|
|
|
|
|
|
|
r = sd_bus_message_read_basic(m, type, &b);
|
|
|
|
if (r < 0)
|
|
|
|
return bus_log_parse_error(r);
|
|
|
|
|
|
|
|
r = json_variant_new_integer(&v, b);
|
|
|
|
if (r < 0)
|
|
|
|
return log_error_errno(r, "Failed to transform int32: %m");
|
|
|
|
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
case SD_BUS_TYPE_UINT32: {
|
|
|
|
uint32_t b;
|
|
|
|
|
|
|
|
r = sd_bus_message_read_basic(m, type, &b);
|
|
|
|
if (r < 0)
|
|
|
|
return bus_log_parse_error(r);
|
|
|
|
|
|
|
|
r = json_variant_new_unsigned(&v, b);
|
|
|
|
if (r < 0)
|
|
|
|
return log_error_errno(r, "Failed to transform uint32: %m");
|
|
|
|
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
case SD_BUS_TYPE_INT64: {
|
|
|
|
int64_t b;
|
|
|
|
|
|
|
|
r = sd_bus_message_read_basic(m, type, &b);
|
|
|
|
if (r < 0)
|
|
|
|
return bus_log_parse_error(r);
|
|
|
|
|
|
|
|
r = json_variant_new_integer(&v, b);
|
|
|
|
if (r < 0)
|
|
|
|
return log_error_errno(r, "Failed to transform int64: %m");
|
|
|
|
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
case SD_BUS_TYPE_UINT64: {
|
|
|
|
uint64_t b;
|
|
|
|
|
|
|
|
r = sd_bus_message_read_basic(m, type, &b);
|
|
|
|
if (r < 0)
|
|
|
|
return bus_log_parse_error(r);
|
|
|
|
|
|
|
|
r = json_variant_new_unsigned(&v, b);
|
|
|
|
if (r < 0)
|
|
|
|
return log_error_errno(r, "Failed to transform uint64: %m");
|
|
|
|
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
case SD_BUS_TYPE_DOUBLE: {
|
|
|
|
double d;
|
|
|
|
|
|
|
|
r = sd_bus_message_read_basic(m, type, &d);
|
|
|
|
if (r < 0)
|
|
|
|
return bus_log_parse_error(r);
|
|
|
|
|
|
|
|
r = json_variant_new_real(&v, d);
|
|
|
|
if (r < 0)
|
|
|
|
return log_error_errno(r, "Failed to transform double: %m");
|
|
|
|
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
case SD_BUS_TYPE_STRING:
|
|
|
|
case SD_BUS_TYPE_OBJECT_PATH:
|
|
|
|
case SD_BUS_TYPE_SIGNATURE: {
|
|
|
|
const char *s;
|
|
|
|
|
|
|
|
r = sd_bus_message_read_basic(m, type, &s);
|
|
|
|
if (r < 0)
|
|
|
|
return bus_log_parse_error(r);
|
|
|
|
|
|
|
|
r = json_variant_new_string(&v, s);
|
|
|
|
if (r < 0)
|
|
|
|
return log_error_errno(r, "Failed to transform double: %m");
|
|
|
|
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
case SD_BUS_TYPE_UNIX_FD:
|
|
|
|
r = sd_bus_message_read_basic(m, type, NULL);
|
|
|
|
if (r < 0)
|
|
|
|
return bus_log_parse_error(r);
|
|
|
|
|
|
|
|
r = json_variant_new_null(&v);
|
|
|
|
if (r < 0)
|
|
|
|
return log_error_errno(r, "Failed to transform fd: %m");
|
|
|
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
case SD_BUS_TYPE_ARRAY:
|
|
|
|
case SD_BUS_TYPE_VARIANT:
|
|
|
|
case SD_BUS_TYPE_STRUCT:
|
|
|
|
r = sd_bus_message_enter_container(m, type, contents);
|
|
|
|
if (r < 0)
|
|
|
|
return bus_log_parse_error(r);
|
|
|
|
|
|
|
|
if (type == SD_BUS_TYPE_VARIANT)
|
|
|
|
r = json_transform_variant(m, contents, &v);
|
|
|
|
else if (type == SD_BUS_TYPE_ARRAY && contents[0] == '{')
|
|
|
|
r = json_transform_dict_array(m, &v);
|
|
|
|
else
|
|
|
|
r = json_transform_array_or_struct(m, &v);
|
|
|
|
if (r < 0)
|
|
|
|
return r;
|
|
|
|
|
|
|
|
r = sd_bus_message_exit_container(m);
|
|
|
|
if (r < 0)
|
|
|
|
return bus_log_parse_error(r);
|
|
|
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
default:
|
|
|
|
assert_not_reached("Unexpected element type");
|
|
|
|
}
|
|
|
|
|
|
|
|
*ret = TAKE_PTR(v);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int json_transform_message(sd_bus_message *m, JsonVariant **ret) {
|
|
|
|
_cleanup_(json_variant_unrefp) JsonVariant *v = NULL;
|
|
|
|
const char *type;
|
|
|
|
int r;
|
|
|
|
|
|
|
|
assert(m);
|
|
|
|
assert(ret);
|
|
|
|
|
|
|
|
assert_se(type = sd_bus_message_get_signature(m, false));
|
|
|
|
|
|
|
|
r = json_transform_array_or_struct(m, &v);
|
|
|
|
if (r < 0)
|
|
|
|
return r;
|
|
|
|
|
|
|
|
r = json_build(ret, JSON_BUILD_OBJECT(JSON_BUILD_PAIR("type", JSON_BUILD_STRING(type)),
|
|
|
|
JSON_BUILD_PAIR("data", JSON_BUILD_VARIANT(v))));
|
|
|
|
if (r < 0)
|
|
|
|
return log_oom();
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void json_dump_with_flags(JsonVariant *v, FILE *f) {
|
|
|
|
|
|
|
|
json_variant_dump(v,
|
|
|
|
(arg_json == JSON_PRETTY ? JSON_FORMAT_PRETTY : JSON_FORMAT_NEWLINE) |
|
|
|
|
colors_enabled() * JSON_FORMAT_COLOR,
|
|
|
|
f, NULL);
|
|
|
|
}
|
|
|
|
|
2018-03-13 21:09:16 +01:00
|
|
|
static int call(int argc, char **argv, void *userdata) {
|
|
|
|
_cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL;
|
tree-wide: expose "p"-suffix unref calls in public APIs to make gcc cleanup easy
GLIB has recently started to officially support the gcc cleanup
attribute in its public API, hence let's do the same for our APIs.
With this patch we'll define an xyz_unrefp() call for each public
xyz_unref() call, to make it easy to use inside a
__attribute__((cleanup())) expression. Then, all code is ported over to
make use of this.
The new calls are also documented in the man pages, with examples how to
use them (well, I only added docs where the _unref() call itself already
had docs, and the examples, only cover sd_bus_unrefp() and
sd_event_unrefp()).
This also renames sd_lldp_free() to sd_lldp_unref(), since that's how we
tend to call our destructors these days.
Note that this defines no public macro that wraps gcc's attribute and
makes it easier to use. While I think it's our duty in the library to
make our stuff easy to use, I figure it's not our duty to make gcc's own
features easy to use on its own. Most likely, client code which wants to
make use of this should define its own:
#define _cleanup_(function) __attribute__((cleanup(function)))
Or similar, to make the gcc feature easier to use.
Making this logic public has the benefit that we can remove three header
files whose only purpose was to define these functions internally.
See #2008.
2015-11-27 19:13:45 +01:00
|
|
|
_cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
|
|
|
|
_cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL, *reply = NULL;
|
2014-11-14 13:11:10 +01:00
|
|
|
int r;
|
|
|
|
|
2018-03-13 21:09:16 +01:00
|
|
|
r = acquire_bus(false, &bus);
|
|
|
|
if (r < 0)
|
|
|
|
return r;
|
2014-11-14 13:11:10 +01:00
|
|
|
|
|
|
|
r = sd_bus_message_new_method_call(bus, &m, argv[1], argv[2], argv[3], argv[4]);
|
2014-11-20 20:59:57 +01:00
|
|
|
if (r < 0)
|
|
|
|
return bus_log_create_error(r);
|
2014-11-14 13:11:10 +01:00
|
|
|
|
2014-11-21 20:13:26 +01:00
|
|
|
r = sd_bus_message_set_expect_reply(m, arg_expect_reply);
|
|
|
|
if (r < 0)
|
|
|
|
return bus_log_create_error(r);
|
|
|
|
|
|
|
|
r = sd_bus_message_set_auto_start(m, arg_auto_start);
|
|
|
|
if (r < 0)
|
|
|
|
return bus_log_create_error(r);
|
|
|
|
|
|
|
|
r = sd_bus_message_set_allow_interactive_authorization(m, arg_allow_interactive_authorization);
|
|
|
|
if (r < 0)
|
|
|
|
return bus_log_create_error(r);
|
|
|
|
|
2014-11-14 13:11:10 +01:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-11-21 20:13:26 +01:00
|
|
|
if (!arg_expect_reply) {
|
|
|
|
r = sd_bus_send(bus, m, NULL);
|
2018-06-14 14:54:32 +02:00
|
|
|
if (r < 0)
|
|
|
|
return log_error_errno(r, "Failed to send message: %m");
|
2014-11-21 20:13:26 +01:00
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2014-11-21 20:31:50 +01:00
|
|
|
r = sd_bus_call(bus, m, arg_timeout, &error, &reply);
|
2018-06-14 14:54:32 +02:00
|
|
|
if (r < 0)
|
|
|
|
return log_error_errno(r, "%s", bus_error_message(&error, r));
|
2014-11-14 13:11:10 +01:00
|
|
|
|
|
|
|
r = sd_bus_message_is_empty(reply);
|
|
|
|
if (r < 0)
|
|
|
|
return bus_log_parse_error(r);
|
2014-11-20 23:12:29 +01:00
|
|
|
|
2014-11-14 13:11:10 +01:00
|
|
|
if (r == 0 && !arg_quiet) {
|
2014-11-20 23:12:29 +01:00
|
|
|
|
2018-07-04 15:28:09 +02:00
|
|
|
if (arg_json != JSON_OFF) {
|
|
|
|
_cleanup_(json_variant_unrefp) JsonVariant *v = NULL;
|
|
|
|
|
|
|
|
if (arg_json != JSON_SHORT)
|
|
|
|
(void) pager_open(arg_no_pager, false);
|
|
|
|
|
|
|
|
r = json_transform_message(reply, &v);
|
|
|
|
if (r < 0)
|
|
|
|
return r;
|
|
|
|
|
|
|
|
json_dump_with_flags(v, stdout);
|
|
|
|
|
|
|
|
} else if (arg_verbose) {
|
2018-03-14 09:48:29 +01:00
|
|
|
(void) pager_open(arg_no_pager, false);
|
2014-11-20 23:12:29 +01:00
|
|
|
|
|
|
|
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);
|
|
|
|
}
|
2014-11-14 17:20:04 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2018-03-13 21:09:16 +01:00
|
|
|
static int get_property(int argc, char **argv, void *userdata) {
|
|
|
|
_cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL;
|
tree-wide: expose "p"-suffix unref calls in public APIs to make gcc cleanup easy
GLIB has recently started to officially support the gcc cleanup
attribute in its public API, hence let's do the same for our APIs.
With this patch we'll define an xyz_unrefp() call for each public
xyz_unref() call, to make it easy to use inside a
__attribute__((cleanup())) expression. Then, all code is ported over to
make use of this.
The new calls are also documented in the man pages, with examples how to
use them (well, I only added docs where the _unref() call itself already
had docs, and the examples, only cover sd_bus_unrefp() and
sd_event_unrefp()).
This also renames sd_lldp_free() to sd_lldp_unref(), since that's how we
tend to call our destructors these days.
Note that this defines no public macro that wraps gcc's attribute and
makes it easier to use. While I think it's our duty in the library to
make our stuff easy to use, I figure it's not our duty to make gcc's own
features easy to use on its own. Most likely, client code which wants to
make use of this should define its own:
#define _cleanup_(function) __attribute__((cleanup(function)))
Or similar, to make the gcc feature easier to use.
Making this logic public has the benefit that we can remove three header
files whose only purpose was to define these functions internally.
See #2008.
2015-11-27 19:13:45 +01:00
|
|
|
_cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
|
2014-11-20 23:12:29 +01:00
|
|
|
char **i;
|
2014-11-14 17:20:04 +01:00
|
|
|
int r;
|
|
|
|
|
2018-03-13 21:09:16 +01:00
|
|
|
r = acquire_bus(false, &bus);
|
|
|
|
if (r < 0)
|
|
|
|
return r;
|
2014-11-14 17:20:04 +01:00
|
|
|
|
2014-11-20 23:12:29 +01:00
|
|
|
STRV_FOREACH(i, argv + 4) {
|
tree-wide: expose "p"-suffix unref calls in public APIs to make gcc cleanup easy
GLIB has recently started to officially support the gcc cleanup
attribute in its public API, hence let's do the same for our APIs.
With this patch we'll define an xyz_unrefp() call for each public
xyz_unref() call, to make it easy to use inside a
__attribute__((cleanup())) expression. Then, all code is ported over to
make use of this.
The new calls are also documented in the man pages, with examples how to
use them (well, I only added docs where the _unref() call itself already
had docs, and the examples, only cover sd_bus_unrefp() and
sd_event_unrefp()).
This also renames sd_lldp_free() to sd_lldp_unref(), since that's how we
tend to call our destructors these days.
Note that this defines no public macro that wraps gcc's attribute and
makes it easier to use. While I think it's our duty in the library to
make our stuff easy to use, I figure it's not our duty to make gcc's own
features easy to use on its own. Most likely, client code which wants to
make use of this should define its own:
#define _cleanup_(function) __attribute__((cleanup(function)))
Or similar, to make the gcc feature easier to use.
Making this logic public has the benefit that we can remove three header
files whose only purpose was to define these functions internally.
See #2008.
2015-11-27 19:13:45 +01:00
|
|
|
_cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
|
2014-11-20 23:12:29 +01:00
|
|
|
const char *contents = NULL;
|
|
|
|
char type;
|
2014-11-14 17:20:04 +01:00
|
|
|
|
2014-11-20 23:12:29 +01:00
|
|
|
r = sd_bus_call_method(bus, argv[1], argv[2], "org.freedesktop.DBus.Properties", "Get", &error, &reply, "ss", argv[3], *i);
|
2018-06-14 14:54:32 +02:00
|
|
|
if (r < 0)
|
|
|
|
return log_error_errno(r, "%s", bus_error_message(&error, r));
|
2014-11-14 17:20:04 +01:00
|
|
|
|
2014-11-20 23:12:29 +01:00
|
|
|
r = sd_bus_message_peek_type(reply, &type, &contents);
|
2014-11-14 17:20:04 +01:00
|
|
|
if (r < 0)
|
|
|
|
return bus_log_parse_error(r);
|
|
|
|
|
2014-11-20 23:12:29 +01:00
|
|
|
r = sd_bus_message_enter_container(reply, 'v', contents);
|
|
|
|
if (r < 0)
|
|
|
|
return bus_log_parse_error(r);
|
2014-11-14 17:20:04 +01:00
|
|
|
|
2018-07-04 15:28:09 +02:00
|
|
|
if (arg_json != JSON_OFF) {
|
|
|
|
_cleanup_(json_variant_unrefp) JsonVariant *v = NULL;
|
|
|
|
|
|
|
|
if (arg_json != JSON_SHORT)
|
|
|
|
(void) pager_open(arg_no_pager, false);
|
|
|
|
|
|
|
|
r = json_transform_variant(reply, contents, &v);
|
|
|
|
if (r < 0)
|
|
|
|
return r;
|
|
|
|
|
|
|
|
json_dump_with_flags(v, stdout);
|
|
|
|
|
|
|
|
} else if (arg_verbose) {
|
2018-03-14 09:48:29 +01:00
|
|
|
(void) pager_open(arg_no_pager, false);
|
2014-11-14 17:20:04 +01:00
|
|
|
|
2014-11-20 23:12:29 +01:00
|
|
|
r = bus_message_dump(reply, stdout, BUS_MESSAGE_DUMP_SUBTREE_ONLY);
|
2014-11-14 17:20:04 +01:00
|
|
|
if (r < 0)
|
2014-11-20 23:12:29 +01:00
|
|
|
return r;
|
|
|
|
} else {
|
|
|
|
fputs(contents, stdout);
|
|
|
|
fputc(' ', stdout);
|
2014-11-14 17:20:04 +01:00
|
|
|
|
2014-11-20 23:12:29 +01:00
|
|
|
r = format_cmdline(reply, stdout, false);
|
2014-11-14 17:20:04 +01:00
|
|
|
if (r < 0)
|
|
|
|
return bus_log_parse_error(r);
|
|
|
|
|
2014-11-20 23:12:29 +01:00
|
|
|
fputc('\n', stdout);
|
2014-11-14 17:20:04 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
r = sd_bus_message_exit_container(reply);
|
|
|
|
if (r < 0)
|
|
|
|
return bus_log_parse_error(r);
|
2014-11-20 23:12:29 +01:00
|
|
|
}
|
2014-11-14 17:20:04 +01:00
|
|
|
|
2014-11-20 23:12:29 +01:00
|
|
|
return 0;
|
|
|
|
}
|
2014-11-14 17:20:04 +01:00
|
|
|
|
2018-03-13 21:09:16 +01:00
|
|
|
static int set_property(int argc, char **argv, void *userdata) {
|
|
|
|
_cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL;
|
tree-wide: expose "p"-suffix unref calls in public APIs to make gcc cleanup easy
GLIB has recently started to officially support the gcc cleanup
attribute in its public API, hence let's do the same for our APIs.
With this patch we'll define an xyz_unrefp() call for each public
xyz_unref() call, to make it easy to use inside a
__attribute__((cleanup())) expression. Then, all code is ported over to
make use of this.
The new calls are also documented in the man pages, with examples how to
use them (well, I only added docs where the _unref() call itself already
had docs, and the examples, only cover sd_bus_unrefp() and
sd_event_unrefp()).
This also renames sd_lldp_free() to sd_lldp_unref(), since that's how we
tend to call our destructors these days.
Note that this defines no public macro that wraps gcc's attribute and
makes it easier to use. While I think it's our duty in the library to
make our stuff easy to use, I figure it's not our duty to make gcc's own
features easy to use on its own. Most likely, client code which wants to
make use of this should define its own:
#define _cleanup_(function) __attribute__((cleanup(function)))
Or similar, to make the gcc feature easier to use.
Making this logic public has the benefit that we can remove three header
files whose only purpose was to define these functions internally.
See #2008.
2015-11-27 19:13:45 +01:00
|
|
|
_cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL;
|
|
|
|
_cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
|
2014-11-20 23:12:29 +01:00
|
|
|
char **p;
|
|
|
|
int r;
|
2014-11-14 17:20:04 +01:00
|
|
|
|
2018-03-13 21:09:16 +01:00
|
|
|
r = acquire_bus(false, &bus);
|
|
|
|
if (r < 0)
|
|
|
|
return r;
|
2014-11-14 17:20:04 +01:00
|
|
|
|
2014-11-20 23:12:29 +01:00
|
|
|
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);
|
2014-11-14 17:20:04 +01:00
|
|
|
|
2014-11-20 23:12:29 +01:00
|
|
|
r = sd_bus_message_append(m, "ss", argv[3], argv[4]);
|
|
|
|
if (r < 0)
|
|
|
|
return bus_log_create_error(r);
|
2014-11-14 17:20:04 +01:00
|
|
|
|
2014-11-20 23:12:29 +01:00
|
|
|
r = sd_bus_message_open_container(m, 'v', argv[5]);
|
|
|
|
if (r < 0)
|
|
|
|
return bus_log_create_error(r);
|
|
|
|
|
2018-03-13 21:09:16 +01:00
|
|
|
p = argv + 6;
|
2014-11-20 23:12:29 +01:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2014-11-21 20:31:50 +01:00
|
|
|
r = sd_bus_call(bus, m, arg_timeout, &error, NULL);
|
2018-06-14 14:54:32 +02:00
|
|
|
if (r < 0)
|
|
|
|
return log_error_errno(r, "%s", bus_error_message(&error, r));
|
2014-11-14 13:11:10 +01:00
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2013-04-24 17:56:28 +02:00
|
|
|
static int help(void) {
|
2018-08-09 10:32:31 +02:00
|
|
|
_cleanup_free_ char *link = NULL;
|
|
|
|
int r;
|
|
|
|
|
|
|
|
r = terminal_urlify_man("busctl", "1", &link);
|
|
|
|
if (r < 0)
|
|
|
|
return log_oom();
|
|
|
|
|
2013-04-24 17:56:28 +02:00
|
|
|
printf("%s [OPTIONS...] {COMMAND} ...\n\n"
|
|
|
|
"Introspect the bus.\n\n"
|
2013-11-06 17:33:24 +01:00
|
|
|
" -h --help Show this help\n"
|
|
|
|
" --version Show package version\n"
|
2013-11-07 21:31:13 +01:00
|
|
|
" --no-pager Do not pipe output into a pager\n"
|
2014-02-19 17:35:35 +01:00
|
|
|
" --no-legend Do not show the headers and footers\n"
|
2013-11-06 17:33:24 +01:00
|
|
|
" --system Connect to system bus\n"
|
|
|
|
" --user Connect to user bus\n"
|
|
|
|
" -H --host=[USER@]HOST Operate on remote host\n"
|
|
|
|
" -M --machine=CONTAINER Operate on local container\n"
|
|
|
|
" --address=ADDRESS Connect to bus specified by address\n"
|
2013-12-24 18:15:38 +01:00
|
|
|
" --show-machine Show machine ID column in list\n"
|
|
|
|
" --unique Only show unique names\n"
|
|
|
|
" --acquired Only show acquired names\n"
|
|
|
|
" --activatable Only show activatable names\n"
|
2014-11-10 19:24:48 +01:00
|
|
|
" --match=MATCH Only show matching messages\n"
|
2015-07-31 17:38:22 +02:00
|
|
|
" --size=SIZE Maximum length of captured packet\n"
|
2014-11-14 13:11:10 +01:00
|
|
|
" --list Don't show tree, but simple object path list\n"
|
2017-12-03 18:38:18 +01:00
|
|
|
" -q --quiet Don't show method call reply\n"
|
2014-11-21 20:13:26 +01:00
|
|
|
" --verbose Show result values in long format\n"
|
2018-07-04 15:28:09 +02:00
|
|
|
" --json=MODE Output as JSON\n"
|
|
|
|
" -j Same as --json=pretty on tty, --json=short otherwise\n"
|
2014-11-21 20:13:26 +01:00
|
|
|
" --expect-reply=BOOL Expect a method call reply\n"
|
|
|
|
" --auto-start=BOOL Auto-start destination service\n"
|
|
|
|
" --allow-interactive-authorization=BOOL\n"
|
2014-11-21 20:31:50 +01:00
|
|
|
" Allow interactive authorization for operation\n"
|
2014-11-24 21:53:29 +01:00
|
|
|
" --timeout=SECS Maximum time to wait for method call completion\n"
|
2017-12-15 22:19:34 +01:00
|
|
|
" --augment-creds=BOOL Extend credential data with data read from /proc/$PID\n"
|
|
|
|
" --watch-bind=BOOL Wait for bus AF_UNIX socket to be bound in the file\n"
|
|
|
|
" system\n\n"
|
2013-04-24 17:56:28 +02:00
|
|
|
"Commands:\n"
|
2013-11-06 17:33:24 +01:00
|
|
|
" list List bus names\n"
|
2014-11-25 02:11:57 +01:00
|
|
|
" status [SERVICE] Show bus service, process or bus owner credentials\n"
|
2013-12-06 13:56:23 +01:00
|
|
|
" monitor [SERVICE...] Show bus traffic\n"
|
2014-10-30 01:13:11 +01:00
|
|
|
" capture [SERVICE...] Capture bus traffic as pcap\n"
|
2014-11-19 21:12:54 +01:00
|
|
|
" tree [SERVICE...] Show object tree of service\n"
|
2014-12-23 22:42:55 +01:00
|
|
|
" introspect SERVICE OBJECT [INTERFACE]\n"
|
2014-11-19 21:12:54 +01:00
|
|
|
" call SERVICE OBJECT INTERFACE METHOD [SIGNATURE [ARGUMENT...]]\n"
|
2014-11-14 13:11:10 +01:00
|
|
|
" Call a method\n"
|
2014-11-20 23:12:29 +01:00
|
|
|
" get-property SERVICE OBJECT INTERFACE PROPERTY...\n"
|
2014-11-14 17:20:04 +01:00
|
|
|
" Get property value\n"
|
2014-11-20 23:12:29 +01:00
|
|
|
" set-property SERVICE OBJECT INTERFACE PROPERTY SIGNATURE ARGUMENT...\n"
|
|
|
|
" Set property value\n"
|
2014-08-02 17:12:21 +02:00
|
|
|
" help Show this help\n"
|
2018-08-09 10:32:31 +02:00
|
|
|
"\nSee the %s for details.\n"
|
|
|
|
, program_invocation_short_name
|
|
|
|
, link
|
|
|
|
);
|
2013-04-24 17:56:28 +02:00
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2018-03-13 21:09:16 +01:00
|
|
|
static int verb_help(int argc, char **argv, void *userdata) {
|
|
|
|
return help();
|
|
|
|
}
|
|
|
|
|
2013-04-24 17:56:28 +02:00
|
|
|
static int parse_argv(int argc, char *argv[]) {
|
|
|
|
|
|
|
|
enum {
|
|
|
|
ARG_VERSION = 0x100,
|
|
|
|
ARG_NO_PAGER,
|
2014-02-19 17:35:35 +01:00
|
|
|
ARG_NO_LEGEND,
|
2013-04-24 17:56:28 +02:00
|
|
|
ARG_SYSTEM,
|
|
|
|
ARG_USER,
|
|
|
|
ARG_ADDRESS,
|
|
|
|
ARG_MATCH,
|
2013-12-24 18:15:38 +01:00
|
|
|
ARG_SHOW_MACHINE,
|
|
|
|
ARG_UNIQUE,
|
|
|
|
ARG_ACQUIRED,
|
2014-10-30 01:13:11 +01:00
|
|
|
ARG_ACTIVATABLE,
|
|
|
|
ARG_SIZE,
|
2014-11-10 19:24:48 +01:00
|
|
|
ARG_LIST,
|
2014-11-20 23:12:29 +01:00
|
|
|
ARG_VERBOSE,
|
2014-11-21 20:13:26 +01:00
|
|
|
ARG_EXPECT_REPLY,
|
|
|
|
ARG_AUTO_START,
|
|
|
|
ARG_ALLOW_INTERACTIVE_AUTHORIZATION,
|
2014-11-21 20:31:50 +01:00
|
|
|
ARG_TIMEOUT,
|
2014-11-24 21:53:29 +01:00
|
|
|
ARG_AUGMENT_CREDS,
|
2017-12-15 22:19:34 +01:00
|
|
|
ARG_WATCH_BIND,
|
2018-07-04 15:28:09 +02:00
|
|
|
ARG_JSON,
|
2013-04-24 17:56:28 +02:00
|
|
|
};
|
|
|
|
|
|
|
|
static const struct option options[] = {
|
2018-07-04 15:28:09 +02:00
|
|
|
{ "help", no_argument, NULL, 'h' },
|
|
|
|
{ "version", no_argument, NULL, ARG_VERSION },
|
|
|
|
{ "no-pager", no_argument, NULL, ARG_NO_PAGER },
|
|
|
|
{ "no-legend", no_argument, NULL, ARG_NO_LEGEND },
|
|
|
|
{ "system", no_argument, NULL, ARG_SYSTEM },
|
|
|
|
{ "user", no_argument, NULL, ARG_USER },
|
|
|
|
{ "address", required_argument, NULL, ARG_ADDRESS },
|
|
|
|
{ "show-machine", no_argument, NULL, ARG_SHOW_MACHINE },
|
|
|
|
{ "unique", no_argument, NULL, ARG_UNIQUE },
|
|
|
|
{ "acquired", no_argument, NULL, ARG_ACQUIRED },
|
|
|
|
{ "activatable", no_argument, NULL, ARG_ACTIVATABLE },
|
|
|
|
{ "match", required_argument, NULL, ARG_MATCH },
|
|
|
|
{ "host", required_argument, NULL, 'H' },
|
|
|
|
{ "machine", required_argument, NULL, 'M' },
|
|
|
|
{ "size", required_argument, NULL, ARG_SIZE },
|
|
|
|
{ "list", no_argument, NULL, ARG_LIST },
|
|
|
|
{ "quiet", no_argument, NULL, 'q' },
|
|
|
|
{ "verbose", no_argument, NULL, ARG_VERBOSE },
|
|
|
|
{ "expect-reply", required_argument, NULL, ARG_EXPECT_REPLY },
|
|
|
|
{ "auto-start", required_argument, NULL, ARG_AUTO_START },
|
2014-11-21 20:13:26 +01:00
|
|
|
{ "allow-interactive-authorization", required_argument, NULL, ARG_ALLOW_INTERACTIVE_AUTHORIZATION },
|
2018-07-04 15:28:09 +02:00
|
|
|
{ "timeout", required_argument, NULL, ARG_TIMEOUT },
|
|
|
|
{ "augment-creds", required_argument, NULL, ARG_AUGMENT_CREDS },
|
|
|
|
{ "watch-bind", required_argument, NULL, ARG_WATCH_BIND },
|
|
|
|
{ "json", required_argument, NULL, ARG_JSON },
|
2013-11-06 18:28:39 +01:00
|
|
|
{},
|
2013-04-24 17:56:28 +02:00
|
|
|
};
|
|
|
|
|
2014-10-30 01:13:11 +01:00
|
|
|
int c, r;
|
2013-04-24 17:56:28 +02:00
|
|
|
|
|
|
|
assert(argc >= 0);
|
|
|
|
assert(argv);
|
|
|
|
|
2018-07-04 15:28:09 +02:00
|
|
|
while ((c = getopt_long(argc, argv, "hH:M:qj", options, NULL)) >= 0)
|
2013-04-24 17:56:28 +02:00
|
|
|
|
|
|
|
switch (c) {
|
|
|
|
|
|
|
|
case 'h':
|
|
|
|
return help();
|
|
|
|
|
|
|
|
case ARG_VERSION:
|
2015-09-23 03:01:06 +02:00
|
|
|
return version();
|
2013-04-24 17:56:28 +02:00
|
|
|
|
|
|
|
case ARG_NO_PAGER:
|
|
|
|
arg_no_pager = true;
|
|
|
|
break;
|
|
|
|
|
2014-02-19 17:35:35 +01:00
|
|
|
case ARG_NO_LEGEND:
|
|
|
|
arg_legend = false;
|
|
|
|
break;
|
|
|
|
|
2013-04-24 17:56:28 +02:00
|
|
|
case ARG_USER:
|
|
|
|
arg_user = true;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case ARG_SYSTEM:
|
|
|
|
arg_user = false;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case ARG_ADDRESS:
|
|
|
|
arg_address = optarg;
|
|
|
|
break;
|
|
|
|
|
2013-12-24 18:15:38 +01:00
|
|
|
case ARG_SHOW_MACHINE:
|
|
|
|
arg_show_machine = true;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case ARG_UNIQUE:
|
|
|
|
arg_unique = true;
|
2013-04-24 17:56:28 +02:00
|
|
|
break;
|
|
|
|
|
2013-12-24 18:15:38 +01:00
|
|
|
case ARG_ACQUIRED:
|
|
|
|
arg_acquired = true;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case ARG_ACTIVATABLE:
|
|
|
|
arg_activatable = true;
|
2013-11-28 20:41:55 +01:00
|
|
|
break;
|
|
|
|
|
2013-04-24 17:56:28 +02:00
|
|
|
case ARG_MATCH:
|
|
|
|
if (strv_extend(&arg_matches, optarg) < 0)
|
|
|
|
return log_oom();
|
|
|
|
break;
|
|
|
|
|
2014-10-30 01:13:11 +01:00
|
|
|
case ARG_SIZE: {
|
2015-09-10 18:16:18 +02:00
|
|
|
uint64_t sz;
|
2014-10-30 01:13:11 +01:00
|
|
|
|
2015-09-10 18:16:18 +02:00
|
|
|
r = parse_size(optarg, 1024, &sz);
|
2018-06-14 14:54:32 +02:00
|
|
|
if (r < 0)
|
|
|
|
return log_error_errno(r, "Failed to parse size: %s", optarg);
|
2014-10-30 01:13:11 +01:00
|
|
|
|
2015-09-10 18:16:18 +02:00
|
|
|
if ((uint64_t) (size_t) sz != sz) {
|
2014-10-30 01:13:11 +01:00
|
|
|
log_error("Size out of range.");
|
|
|
|
return -E2BIG;
|
|
|
|
}
|
|
|
|
|
2015-09-10 18:16:18 +02:00
|
|
|
arg_snaplen = (size_t) sz;
|
2014-10-30 01:13:11 +01:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
2014-11-10 19:24:48 +01:00
|
|
|
case ARG_LIST:
|
|
|
|
arg_list = true;
|
|
|
|
break;
|
|
|
|
|
2013-11-06 17:33:24 +01:00
|
|
|
case 'H':
|
|
|
|
arg_transport = BUS_TRANSPORT_REMOTE;
|
|
|
|
arg_host = optarg;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case 'M':
|
2014-12-23 23:38:13 +01:00
|
|
|
arg_transport = BUS_TRANSPORT_MACHINE;
|
2013-11-06 17:33:24 +01:00
|
|
|
arg_host = optarg;
|
|
|
|
break;
|
|
|
|
|
2014-11-14 13:11:10 +01:00
|
|
|
case 'q':
|
|
|
|
arg_quiet = true;
|
|
|
|
break;
|
|
|
|
|
2014-11-20 23:12:29 +01:00
|
|
|
case ARG_VERBOSE:
|
|
|
|
arg_verbose = true;
|
|
|
|
break;
|
|
|
|
|
2014-11-21 20:13:26 +01:00
|
|
|
case ARG_EXPECT_REPLY:
|
|
|
|
r = parse_boolean(optarg);
|
2018-06-14 14:54:32 +02:00
|
|
|
if (r < 0)
|
|
|
|
return log_error_errno(r, "Failed to parse --expect-reply= parameter: %s", optarg);
|
2014-11-21 20:13:26 +01:00
|
|
|
|
2018-06-11 16:02:03 +02:00
|
|
|
arg_expect_reply = r;
|
2014-11-21 20:13:26 +01:00
|
|
|
break;
|
|
|
|
|
|
|
|
case ARG_AUTO_START:
|
|
|
|
r = parse_boolean(optarg);
|
2018-06-14 14:54:32 +02:00
|
|
|
if (r < 0)
|
|
|
|
return log_error_errno(r, "Failed to parse --auto-start= parameter: %s", optarg);
|
2014-11-21 20:13:26 +01:00
|
|
|
|
2018-06-11 16:02:03 +02:00
|
|
|
arg_auto_start = r;
|
2014-11-21 20:13:26 +01:00
|
|
|
break;
|
|
|
|
|
|
|
|
case ARG_ALLOW_INTERACTIVE_AUTHORIZATION:
|
|
|
|
r = parse_boolean(optarg);
|
2018-06-14 14:54:32 +02:00
|
|
|
if (r < 0)
|
|
|
|
return log_error_errno(r, "Failed to parse --allow-interactive-authorization= parameter: %s", optarg);
|
2014-11-21 20:13:26 +01:00
|
|
|
|
2018-06-11 16:02:03 +02:00
|
|
|
arg_allow_interactive_authorization = r;
|
2014-11-21 20:13:26 +01:00
|
|
|
break;
|
|
|
|
|
2014-11-21 20:31:50 +01:00
|
|
|
case ARG_TIMEOUT:
|
|
|
|
r = parse_sec(optarg, &arg_timeout);
|
2018-06-14 14:54:32 +02:00
|
|
|
if (r < 0)
|
|
|
|
return log_error_errno(r, "Failed to parse --timeout= parameter: %s", optarg);
|
2014-11-21 20:31:50 +01:00
|
|
|
|
|
|
|
break;
|
|
|
|
|
2014-11-24 21:53:29 +01:00
|
|
|
case ARG_AUGMENT_CREDS:
|
|
|
|
r = parse_boolean(optarg);
|
2018-06-14 14:54:32 +02:00
|
|
|
if (r < 0)
|
|
|
|
return log_error_errno(r, "Failed to parse --augment-creds= parameter: %s", optarg);
|
2014-11-24 21:53:29 +01:00
|
|
|
|
2018-06-11 16:02:03 +02:00
|
|
|
arg_augment_creds = r;
|
2014-11-24 21:53:29 +01:00
|
|
|
break;
|
|
|
|
|
2017-12-15 22:19:34 +01:00
|
|
|
case ARG_WATCH_BIND:
|
|
|
|
r = parse_boolean(optarg);
|
2018-06-14 14:54:32 +02:00
|
|
|
if (r < 0)
|
|
|
|
return log_error_errno(r, "Failed to parse --watch-bind= parameter: %s", optarg);
|
2017-12-15 22:19:34 +01:00
|
|
|
|
2018-06-11 16:02:03 +02:00
|
|
|
arg_watch_bind = r;
|
2017-12-15 22:19:34 +01:00
|
|
|
break;
|
|
|
|
|
2018-07-04 15:28:09 +02:00
|
|
|
case 'j':
|
|
|
|
if (on_tty())
|
|
|
|
arg_json = JSON_PRETTY;
|
|
|
|
else
|
|
|
|
arg_json = JSON_SHORT;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case ARG_JSON:
|
|
|
|
if (streq(optarg, "short"))
|
|
|
|
arg_json = JSON_SHORT;
|
|
|
|
else if (streq(optarg, "pretty"))
|
|
|
|
arg_json = JSON_PRETTY;
|
|
|
|
else if (streq(optarg, "help")) {
|
|
|
|
fputs("short\n"
|
|
|
|
"pretty\n", stdout);
|
|
|
|
return 0;
|
|
|
|
} else {
|
|
|
|
log_error("Unknown JSON out mode: %s", optarg);
|
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
|
|
|
|
break;
|
|
|
|
|
2013-04-24 17:56:28 +02:00
|
|
|
case '?':
|
|
|
|
return -EINVAL;
|
|
|
|
|
|
|
|
default:
|
2013-11-06 18:28:39 +01:00
|
|
|
assert_not_reached("Unhandled option");
|
2013-04-24 17:56:28 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
2018-03-13 21:09:16 +01:00
|
|
|
static int busctl_main(int argc, char *argv[]) {
|
|
|
|
|
|
|
|
static const Verb verbs[] = {
|
|
|
|
{ "list", VERB_ANY, 1, VERB_DEFAULT, list_bus_names },
|
|
|
|
{ "status", VERB_ANY, 2, 0, status },
|
|
|
|
{ "monitor", VERB_ANY, VERB_ANY, 0, verb_monitor },
|
|
|
|
{ "capture", VERB_ANY, VERB_ANY, 0, verb_capture },
|
|
|
|
{ "tree", VERB_ANY, VERB_ANY, 0, tree },
|
|
|
|
{ "introspect", 3, 4, 0, introspect },
|
|
|
|
{ "call", 5, VERB_ANY, 0, call },
|
|
|
|
{ "get-property", 5, VERB_ANY, 0, get_property },
|
|
|
|
{ "set-property", 6, VERB_ANY, 0, set_property },
|
|
|
|
{ "help", VERB_ANY, VERB_ANY, 0, verb_help },
|
|
|
|
{}
|
|
|
|
};
|
2013-04-24 17:56:28 +02:00
|
|
|
|
2018-03-13 21:09:16 +01:00
|
|
|
return dispatch_verb(argc, argv, verbs, NULL);
|
2013-04-24 17:56:28 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
int main(int argc, char *argv[]) {
|
|
|
|
int r;
|
|
|
|
|
|
|
|
log_parse_environment();
|
|
|
|
log_open();
|
|
|
|
|
|
|
|
r = parse_argv(argc, argv);
|
|
|
|
if (r <= 0)
|
|
|
|
goto finish;
|
|
|
|
|
2018-03-13 21:09:16 +01:00
|
|
|
r = busctl_main(argc, argv);
|
2013-04-24 17:56:28 +02:00
|
|
|
|
|
|
|
finish:
|
2018-01-12 16:20:36 +01:00
|
|
|
/* make sure we terminate the bus connection first, and then close the
|
|
|
|
* pager, see issue #3543 for the details. */
|
2013-04-24 17:56:28 +02:00
|
|
|
pager_close();
|
2013-11-06 17:33:24 +01:00
|
|
|
|
2018-03-13 21:09:16 +01:00
|
|
|
arg_matches = strv_free(arg_matches);
|
2013-03-19 20:03:16 +01:00
|
|
|
|
|
|
|
return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
|
|
|
|
}
|