X-Git-Url: http://www.chiark.greenend.org.uk/ucgi/~ianmdlvl/git?p=elogind.git;a=blobdiff_plain;f=src%2Flibsystemd%2Fsd-bus%2Fbusctl.c;h=c58a97bc145fe73f08c35c6358debca5b557344b;hp=a59b8eab4fa9f04ab9b7217ccbaf62dc8efee258;hb=a1ad376761af16da46c9ad90fd8df41c8c5c0976;hpb=17d47d8d2dee22ee4f0a7319b9603d3e33a0b28a diff --git a/src/libsystemd/sd-bus/busctl.c b/src/libsystemd/sd-bus/busctl.c index a59b8eab4..c58a97bc1 100644 --- a/src/libsystemd/sd-bus/busctl.c +++ b/src/libsystemd/sd-bus/busctl.c @@ -26,12 +26,16 @@ #include "log.h" #include "build.h" #include "pager.h" +#include "xml.h" +#include "path-util.h" #include "sd-bus.h" #include "bus-message.h" #include "bus-internal.h" #include "bus-util.h" #include "bus-dump.h" +#include "bus-signature.h" +#include "busctl-introspect.h" static bool arg_no_pager = false; static bool arg_legend = true; @@ -44,6 +48,9 @@ static char **arg_matches = NULL; static BusTransport arg_transport = BUS_TRANSPORT_LOCAL; static char *arg_host = NULL; static bool arg_user = false; +static size_t arg_snaplen = 4096; +static bool arg_list = false; +static bool arg_quiet = false; static void pager_open_if_enabled(void) { @@ -68,6 +75,9 @@ static int list_bus_names(sd_bus *bus, char **argv) { assert(bus); + if (!arg_unique && !arg_acquired && !arg_activatable) + arg_unique = arg_acquired = arg_activatable = true; + r = sd_bus_list_names(bus, (arg_acquired || arg_unique) ? &acquired : NULL, arg_activatable ? &activatable : NULL); if (r < 0) { log_error("Failed to list names: %s", strerror(-r)); @@ -76,7 +86,7 @@ static int list_bus_names(sd_bus *bus, char **argv) { pager_open_if_enabled(); - names = hashmap_new(string_hash_func, string_compare_func); + names = hashmap_new(&string_hash_ops); if (!names) return log_oom(); @@ -109,7 +119,7 @@ static int list_bus_names(sd_bus *bus, char **argv) { if (arg_legend) { printf("%-*s %*s %-*s %-*s %-*s %-*s %-*s %-*s", - (int) max_i, "NAME", 10, "PID", 15, "PROCESS", 16, "USER", 13, "CONNECTION", 25, "UNIT", 10, "SESSION", 19, "CONNECTION-NAME"); + (int) max_i, "NAME", 10, "PID", 15, "PROCESS", 16, "USER", 13, "CONNECTION", 25, "UNIT", 10, "SESSION", 19, "DESCRIPTION"); if (arg_show_machine) puts(" MACHINE"); @@ -142,10 +152,10 @@ static int list_bus_names(sd_bus *bus, char **argv) { printf("%-*s", (int) max_i, *i); - r = sd_bus_get_owner(bus, *i, + r = sd_bus_get_name_creds(bus, *i, SD_BUS_CREDS_UID|SD_BUS_CREDS_PID|SD_BUS_CREDS_COMM| SD_BUS_CREDS_UNIQUE_NAME|SD_BUS_CREDS_UNIT|SD_BUS_CREDS_SESSION| - SD_BUS_CREDS_CONNECTION_NAME, &creds); + SD_BUS_CREDS_DESCRIPTION, &creds); if (r >= 0) { const char *unique, *session, *unit, *cn; pid_t pid; @@ -200,7 +210,7 @@ static int list_bus_names(sd_bus *bus, char **argv) { else fputs(" - ", stdout); - r = sd_bus_creds_get_connection_name(creds, &cn); + r = sd_bus_creds_get_description(creds, &cn); if (r >= 0) printf(" %-19s", cn); else @@ -210,7 +220,7 @@ static int list_bus_names(sd_bus *bus, char **argv) { printf(" - - - - - - - "); if (arg_show_machine) { - r = sd_bus_get_owner_machine_id(bus, *i, &mid); + r = sd_bus_get_name_machine_id(bus, *i, &mid); if (r >= 0) { char m[SD_ID128_STRING_MAX]; printf(" %s\n", sd_id128_to_string(mid, m)); @@ -223,7 +233,252 @@ static int list_bus_names(sd_bus *bus, char **argv) { return 0; } -static int monitor(sd_bus *bus, char *argv[]) { +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++; + } + + vertical = strappenda(prefix, draw_special_char(DRAW_TREE_VERTICAL)); + space = strappenda(prefix, draw_special_char(DRAW_TREE_SPACE)); + + 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++; + } + + printf("%s%s%s\n", prefix, draw_special_char(has_more ? DRAW_TREE_BRANCH : DRAW_TREE_RIGHT), *l); + + print_subtree(has_more ? vertical : space, *l, l); + l = n; + } +} + +static void print_tree(const char *prefix, char **l) { + + pager_open_if_enabled(); + + prefix = strempty(prefix); + + if (arg_list) { + char **i; + + STRV_FOREACH(i, l) + printf("%s%s\n", prefix, *i); + return; + } + + if (strv_isempty(l)) { + printf("No objects discovered.\n"); + return; + } + + if (streq(l[0], "/") && !l[1]) { + printf("Only root object discovered.\n"); + return; + } + + print_subtree(prefix, "/", l); +} + +static int on_path(const char *path, void *userdata) { + Set *paths = userdata; + int r; + + assert(paths); + + r = set_put_strdup(paths, path); + if (r < 0) + return log_oom(); + + return 0; +} + +static int find_nodes(sd_bus *bus, const char *service, const char *path, Set *paths) { + const XMLIntrospectOps ops = { + .on_path = on_path, + }; + + _cleanup_bus_message_unref_ sd_bus_message *reply = NULL; + _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL; + const char *xml; + int r; + + r = sd_bus_call_method(bus, service, path, "org.freedesktop.DBus.Introspectable", "Introspect", &error, &reply, ""); + if (r < 0) { + log_error("Failed to introspect object %s of service %s: %s", path, service, bus_error_message(&error, r)); + return r; + } + + r = sd_bus_message_read(reply, "s", &xml); + if (r < 0) + return bus_log_parse_error(r); + + /* fputs(xml, stdout); */ + return parse_xml_introspect(path, xml, &ops, paths); +} + +static int tree_one(sd_bus *bus, const char *service, const char *prefix) { + _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; + + q = find_nodes(bus, service, p, paths); + 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; + } + + l = set_get_strv(done); + if (!l) + return log_oom(); + + strv_sort(l); + print_tree(prefix, l); + + fflush(stdout); + + return r; +} + +static int tree(sd_bus *bus, char **argv) { + char **i; + int r = 0; + + if (!arg_unique && !arg_acquired) + arg_acquired = true; + + if (strv_length(argv) <= 1) { + _cleanup_strv_free_ char **names = NULL; + bool not_first = false; + + r = sd_bus_list_names(bus, &names, NULL); + if (r < 0) { + log_error("Failed to get name list: %s", strerror(-r)); + return r; + } + + pager_open_if_enabled(); + + STRV_FOREACH(i, names) { + int q; + + if (!arg_unique && (*i)[0] == ':') + continue; + + if (!arg_acquired && (*i)[0] == ':') + continue; + + if (not_first) + printf("\n"); + + printf("Service %s%s%s:\n", ansi_highlight(), *i, ansi_highlight_off()); + + q = tree_one(bus, *i, NULL); + if (q < 0 && r >= 0) + r = q; + + not_first = true; + } + } else { + pager_open_if_enabled(); + + STRV_FOREACH(i, argv+1) { + int q; + + if (i > argv+1) + printf("\n"); + + if (argv[2]) + printf("Service %s%s%s:\n", ansi_highlight(), *i, ansi_highlight_off()); + + q = tree_one(bus, *i, NULL); + if (q < 0 && r >= 0) + r = q; + } + } + + return r; +} + +static int message_dump(sd_bus_message *m, FILE *f) { + return bus_message_dump(m, f, BUS_MESSAGE_DUMP_WITH_HEADER); +} + +static int message_pcap(sd_bus_message *m, FILE *f) { + return bus_message_pcap_frame(m, arg_snaplen, f); +} + +static int monitor(sd_bus *bus, char *argv[], int (*dump)(sd_bus_message *m, FILE *f)) { bool added_something = false; char **i; int r; @@ -240,7 +495,7 @@ static int monitor(sd_bus *bus, char *argv[]) { if (!m) return log_oom(); - r = sd_bus_add_match(bus, m, NULL, NULL); + r = sd_bus_add_match(bus, NULL, m, NULL, NULL); if (r < 0) { log_error("Failed to add match: %s", strerror(-r)); return r; @@ -250,7 +505,7 @@ static int monitor(sd_bus *bus, char *argv[]) { } STRV_FOREACH(i, arg_matches) { - r = sd_bus_add_match(bus, *i, NULL, NULL); + r = sd_bus_add_match(bus, NULL, *i, NULL, NULL); if (r < 0) { log_error("Failed to add match: %s", strerror(-r)); return r; @@ -260,7 +515,7 @@ static int monitor(sd_bus *bus, char *argv[]) { } if (!added_something) { - r = sd_bus_add_match(bus, "", NULL, NULL); + r = sd_bus_add_match(bus, NULL, "", NULL, NULL); if (r < 0) { log_error("Failed to add match: %s", strerror(-r)); return r; @@ -277,7 +532,7 @@ static int monitor(sd_bus *bus, char *argv[]) { } if (m) { - bus_message_dump(m, stdout, true); + dump(m, stdout); continue; } @@ -292,6 +547,28 @@ static int monitor(sd_bus *bus, char *argv[]) { } } +static int capture(sd_bus *bus, char *argv[]) { + 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); + + r = monitor(bus, argv, message_pcap); + if (r < 0) + return r; + + if (ferror(stdout)) { + log_error("Couldn't write capture file."); + return -EIO; + } + + return r; +} + static int status(sd_bus *bus, char *argv[]) { _cleanup_bus_creds_unref_ sd_bus_creds *creds = NULL; pid_t pid; @@ -306,9 +583,9 @@ static int status(sd_bus *bus, char *argv[]) { r = parse_pid(argv[1], &pid); if (r < 0) - r = sd_bus_get_owner(bus, argv[1], _SD_BUS_CREDS_ALL, &creds); + r = sd_bus_get_name_creds(bus, argv[1], _SD_BUS_CREDS_ALL, &creds); else - r = sd_bus_creds_new_from_pid(pid, _SD_BUS_CREDS_ALL, &creds); + r = sd_bus_creds_new_from_pid(&creds, pid, _SD_BUS_CREDS_ALL); if (r < 0) { log_error("Failed to get credentials: %s", strerror(-r)); @@ -319,8 +596,411 @@ static int status(sd_bus *bus, char *argv[]) { return 0; } -static int help(void) { +static int message_append_cmdline(sd_bus_message *m, const char *signature, char ***x) { + char **p; + int r; + + assert(m); + assert(signature); + assert(x); + + p = *x; + + for (;;) { + const char *v; + char t; + + t = *signature; + v = *p; + + if (t == 0) + break; + if (!v) { + log_error("Too few parameters for signature."); + return -EINVAL; + } + + signature++; + p++; + + switch (t) { + + case SD_BUS_TYPE_BOOLEAN: + r = parse_boolean(v); + if (r < 0) { + log_error("Failed to parse as boolean: %s", v); + return r; + } + + r = sd_bus_message_append_basic(m, t, &r); + break; + + case SD_BUS_TYPE_BYTE: { + uint8_t z; + + r = safe_atou8(v, &z); + if (r < 0) { + log_error("Failed to parse as byte (unsigned 8bit integer): %s", v); + return r; + } + + r = sd_bus_message_append_basic(m, t, &z); + break; + } + + case SD_BUS_TYPE_INT16: { + int16_t z; + + r = safe_atoi16(v, &z); + if (r < 0) { + log_error("Failed to parse as signed 16bit integer: %s", v); + return r; + } + + r = sd_bus_message_append_basic(m, t, &z); + break; + } + + case SD_BUS_TYPE_UINT16: { + uint16_t z; + + r = safe_atou16(v, &z); + if (r < 0) { + log_error("Failed to parse as unsigned 16bit integer: %s", v); + return r; + } + + r = sd_bus_message_append_basic(m, t, &z); + break; + } + + case SD_BUS_TYPE_INT32: { + int32_t z; + + r = safe_atoi32(v, &z); + if (r < 0) { + log_error("Failed to parse as signed 32bit integer: %s", v); + return r; + } + + r = sd_bus_message_append_basic(m, t, &z); + break; + } + + case SD_BUS_TYPE_UINT32: { + uint32_t z; + + r = safe_atou32(v, &z); + if (r < 0) { + log_error("Failed to parse as unsigned 32bit integer: %s", v); + return r; + } + + r = sd_bus_message_append_basic(m, t, &z); + break; + } + + case SD_BUS_TYPE_INT64: { + int64_t z; + + r = safe_atoi64(v, &z); + if (r < 0) { + log_error("Failed to parse as signed 64bit integer: %s", v); + return r; + } + + r = sd_bus_message_append_basic(m, t, &z); + break; + } + + case SD_BUS_TYPE_UINT64: { + uint64_t z; + + r = safe_atou64(v, &z); + if (r < 0) { + log_error("Failed to parse as unsigned 64bit integer: %s", v); + return r; + } + + r = sd_bus_message_append_basic(m, t, &z); + break; + } + + + case SD_BUS_TYPE_DOUBLE: { + double z; + + r = safe_atod(v, &z); + if (r < 0) { + log_error("Failed to parse as double precision floating point: %s", v); + return r; + } + + r = sd_bus_message_append_basic(m, t, &z); + break; + } + + case SD_BUS_TYPE_STRING: + case SD_BUS_TYPE_OBJECT_PATH: + case SD_BUS_TYPE_SIGNATURE: + + r = sd_bus_message_append_basic(m, t, v); + break; + + case SD_BUS_TYPE_ARRAY: { + uint32_t n; + size_t k; + + r = safe_atou32(v, &n); + if (r < 0) { + log_error("Failed to parse number of array entries: %s", v); + return r; + } + + r = signature_element_length(signature, &k); + if (r < 0) { + log_error("Invalid array signature."); + return r; + } + + { + unsigned i; + char s[k + 1]; + memcpy(s, signature, k); + s[k] = 0; + + r = sd_bus_message_open_container(m, SD_BUS_TYPE_ARRAY, s); + if (r < 0) + return bus_log_create_error(r); + + for (i = 0; i < n; i++) { + r = message_append_cmdline(m, s, &p); + if (r < 0) + return r; + } + } + + signature += k; + + r = sd_bus_message_close_container(m); + break; + } + + case SD_BUS_TYPE_VARIANT: + r = sd_bus_message_open_container(m, SD_BUS_TYPE_VARIANT, v); + if (r < 0) + return bus_log_create_error(r); + + r = message_append_cmdline(m, v, &p); + if (r < 0) + return r; + + r = sd_bus_message_close_container(m); + break; + + case SD_BUS_TYPE_STRUCT_BEGIN: + case SD_BUS_TYPE_DICT_ENTRY_BEGIN: { + size_t k; + + signature--; + p--; + + r = signature_element_length(signature, &k); + if (r < 0) { + log_error("Invalid struct/dict entry signature."); + return r; + } + + { + char s[k-1]; + memcpy(s, signature + 1, k - 2); + s[k - 2] = 0; + + r = sd_bus_message_open_container(m, t == SD_BUS_TYPE_STRUCT_BEGIN ? SD_BUS_TYPE_STRUCT : SD_BUS_TYPE_DICT_ENTRY, s); + if (r < 0) + return bus_log_create_error(r); + + r = message_append_cmdline(m, s, &p); + if (r < 0) + return r; + } + + signature += k; + + r = sd_bus_message_close_container(m); + break; + } + + case SD_BUS_TYPE_UNIX_FD: + log_error("UNIX file descriptor not supported as type."); + return -EINVAL; + + default: + log_error("Unknown signature type %c.", t); + return -EINVAL; + } + + if (r < 0) + return bus_log_create_error(r); + } + + *x = p; + return 0; +} + +static int call(sd_bus *bus, char *argv[]) { + _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL; + _cleanup_bus_message_unref_ sd_bus_message *m = NULL, *reply = NULL; + int r; + + assert(bus); + + if (strv_length(argv) < 5) { + log_error("Expects at least four arguments."); + return -EINVAL; + } + + r = sd_bus_message_new_method_call(bus, &m, argv[1], argv[2], argv[3], argv[4]); + if (r < 0) { + log_error("Failed to prepare bus message: %s", strerror(-r)); + return r; + } + + if (!isempty(argv[5])) { + char **p; + + p = argv+6; + + r = message_append_cmdline(m, argv[5], &p); + if (r < 0) + return r; + + if (*p) { + log_error("Too many parameters for signature."); + return -EINVAL; + } + } + + r = sd_bus_call(bus, m, 0, &error, &reply); + if (r < 0) { + log_error("%s", bus_error_message(&error, r)); + return r; + } + + r = sd_bus_message_is_empty(reply); + if (r < 0) + return bus_log_parse_error(r); + if (r == 0 && !arg_quiet) { + pager_open_if_enabled(); + bus_message_dump(reply, stdout, 0); + } + + return 0; +} + +static int get_property(sd_bus *bus, char *argv[]) { + _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL; + unsigned n; + int r; + + assert(bus); + + n = strv_length(argv); + if (n < 3) { + log_error("Expects at least three arguments."); + return -EINVAL; + } + + if (n < 5) { + _cleanup_bus_message_unref_ sd_bus_message *reply = NULL; + bool not_first = false; + + r = sd_bus_call_method(bus, argv[1], argv[2], "org.freedesktop.DBus.Properties", "GetAll", &error, &reply, "s", strempty(argv[3])); + if (r < 0) { + log_error("%s", bus_error_message(&error, r)); + return r; + } + + r = sd_bus_message_enter_container(reply, 'a', "{sv}"); + if (r < 0) + return bus_log_parse_error(r); + + for (;;) { + const char *name; + + r = sd_bus_message_enter_container(reply, 'e', "sv"); + if (r < 0) + return bus_log_parse_error(r); + + if (r == 0) + break; + + r = sd_bus_message_read(reply, "s", &name); + if (r < 0) + return bus_log_parse_error(r); + + if (not_first) + printf("\n"); + + printf("Property %s:\n", name); + + r = sd_bus_message_enter_container(reply, 'v', NULL); + if (r < 0) + return bus_log_parse_error(r); + + pager_open_if_enabled(); + bus_message_dump(reply, stdout, BUS_MESSAGE_DUMP_SUBTREE_ONLY); + + r = sd_bus_message_exit_container(reply); + if (r < 0) + return bus_log_parse_error(r); + + r = sd_bus_message_exit_container(reply); + if (r < 0) + return bus_log_parse_error(r); + + not_first = true; + } + + r = sd_bus_message_exit_container(reply); + if (r < 0) + return bus_log_parse_error(r); + } else { + char **i; + + STRV_FOREACH(i, argv + 4) { + _cleanup_bus_message_unref_ sd_bus_message *reply = NULL; + + r = sd_bus_call_method(bus, argv[1], argv[2], "org.freedesktop.DBus.Properties", "Get", &error, &reply, "ss", argv[3], *i); + if (r < 0) { + log_error("%s", bus_error_message(&error, r)); + return r; + } + + r = sd_bus_message_enter_container(reply, 'v', NULL); + if (r < 0) + return bus_log_parse_error(r); + + if (i > argv + 4) + printf("\n"); + + if (argv[5]) + printf("Property %s:\n", *i); + + pager_open_if_enabled(); + bus_message_dump(reply, stdout, BUS_MESSAGE_DUMP_SUBTREE_ONLY); + + r = sd_bus_message_exit_container(reply); + if (r < 0) + return bus_log_parse_error(r); + } + } + + return 0; +} + +static int help(void) { printf("%s [OPTIONS...] {COMMAND} ...\n\n" "Introspect the bus.\n\n" " -h --help Show this help\n" @@ -336,13 +1016,21 @@ static int help(void) { " --unique Only show unique names\n" " --acquired Only show acquired names\n" " --activatable Only show activatable names\n" - " --match=MATCH Only show matching messages\n\n" + " --match=MATCH Only show matching messages\n" + " --list Don't show tree, but simple object path list\n" + " --quiet Don't show method call reply\n\n" "Commands:\n" " list List bus names\n" + " tree [SERVICE...] Show object tree of service\n" " monitor [SERVICE...] Show bus traffic\n" - " status NAME Show name status\n" - " help Show this help\n", - program_invocation_short_name); + " capture [SERVICE...] Capture bus traffic as pcap\n" + " status SERVICE Show service name status\n" + " call SERVICE PATH INTERFACE METHOD [SIGNATURE [ARGUMENTS...]]\n" + " Call a method\n" + " get-property SERVICE PATH [INTERFACE [PROPERTY...]]\n" + " Get property value\n" + " help Show this help\n" + , program_invocation_short_name); return 0; } @@ -360,7 +1048,9 @@ static int parse_argv(int argc, char *argv[]) { ARG_SHOW_MACHINE, ARG_UNIQUE, ARG_ACQUIRED, - ARG_ACTIVATABLE + ARG_ACTIVATABLE, + ARG_SIZE, + ARG_LIST, }; static const struct option options[] = { @@ -378,15 +1068,18 @@ static int parse_argv(int argc, char *argv[]) { { "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' }, {}, }; - int c; + int c, r; assert(argc >= 0); assert(argv); - while ((c = getopt_long(argc, argv, "hH:M:", options, NULL)) >= 0) { + while ((c = getopt_long(argc, argv, "hH:M:q", options, NULL)) >= 0) switch (c) { @@ -439,6 +1132,28 @@ static int parse_argv(int argc, char *argv[]) { return log_oom(); break; + case ARG_SIZE: { + off_t o; + + r = parse_size(optarg, 0, &o); + if (r < 0) { + log_error("Failed to parse size: %s", optarg); + return r; + } + + if ((off_t) (size_t) o != o) { + log_error("Size out of range."); + return -E2BIG; + } + + arg_snaplen = (size_t) o; + break; + } + + case ARG_LIST: + arg_list = true; + break; + case 'H': arg_transport = BUS_TRANSPORT_REMOTE; arg_host = optarg; @@ -449,16 +1164,16 @@ static int parse_argv(int argc, char *argv[]) { arg_host = optarg; break; + case 'q': + arg_quiet = true; + break; + case '?': return -EINVAL; default: assert_not_reached("Unhandled option"); } - } - - if (!arg_unique && !arg_acquired && !arg_activatable) - arg_unique = arg_acquired = arg_activatable = true; return 1; } @@ -471,11 +1186,23 @@ static int busctl_main(sd_bus *bus, int argc, char *argv[]) { return list_bus_names(bus, argv + optind); if (streq(argv[optind], "monitor")) - return monitor(bus, argv + optind); + return monitor(bus, argv + optind, message_dump); + + if (streq(argv[optind], "capture")) + return capture(bus, argv + optind); if (streq(argv[optind], "status")) return status(bus, argv + optind); + if (streq(argv[optind], "tree")) + return tree(bus, argv + optind); + + if (streq(argv[optind], "call")) + return call(bus, argv + optind); + + if (streq(argv[optind], "get-property")) + return get_property(bus, argv + optind); + if (streq(argv[optind], "help")) return help(); @@ -484,7 +1211,7 @@ static int busctl_main(sd_bus *bus, int argc, char *argv[]) { } int main(int argc, char *argv[]) { - _cleanup_bus_unref_ sd_bus *bus = NULL; + _cleanup_bus_close_unref_ sd_bus *bus = NULL; int r; log_parse_environment(); @@ -494,29 +1221,76 @@ int main(int argc, char *argv[]) { if (r <= 0) goto finish; - if (arg_address) { - r = sd_bus_new(&bus); + r = sd_bus_new(&bus); + if (r < 0) { + log_error("Failed to allocate bus: %s", strerror(-r)); + goto finish; + } + + if (streq_ptr(argv[optind], "monitor") || + streq_ptr(argv[optind], "capture")) { + + r = sd_bus_set_monitor(bus, true); if (r < 0) { - log_error("Failed to allocate bus: %s", strerror(-r)); + log_error("Failed to set monitor mode: %s", strerror(-r)); goto finish; } - r = sd_bus_set_address(bus, arg_address); + r = sd_bus_negotiate_creds(bus, _SD_BUS_CREDS_ALL); + if (r < 0) { + log_error("Failed to enable credentials: %s", strerror(-r)); + goto finish; + } + + r = sd_bus_negotiate_timestamp(bus, true); if (r < 0) { - log_error("Failed to set address: %s", strerror(-r)); + log_error("Failed to enable timestamps: %s", strerror(-r)); goto finish; } - r = sd_bus_set_bus_client(bus, true); + r = sd_bus_negotiate_fds(bus, true); if (r < 0) { - log_error("Failed to set bus client: %s", strerror(-r)); + log_error("Failed to enable fds: %s", strerror(-r)); goto finish; } + } + + if (arg_address) + r = sd_bus_set_address(bus, arg_address); + else { + switch (arg_transport) { + + case BUS_TRANSPORT_LOCAL: + if (arg_user) + r = bus_set_address_user(bus); + else + r = bus_set_address_system(bus); + break; + + case BUS_TRANSPORT_REMOTE: + r = bus_set_address_system_remote(bus, arg_host); + break; + + case BUS_TRANSPORT_CONTAINER: + r = bus_set_address_system_container(bus, arg_host); + break; - r = sd_bus_start(bus); - } else - r = bus_open_transport(arg_transport, arg_host, arg_user, &bus); + default: + assert_not_reached("Hmm, unknown transport type."); + } + } + if (r < 0) { + log_error("Failed to set address: %s", strerror(-r)); + goto finish; + } + + r = sd_bus_set_bus_client(bus, true); + if (r < 0) { + log_error("Failed to set bus client: %s", strerror(-r)); + goto finish; + } + r = sd_bus_start(bus); if (r < 0) { log_error("Failed to connect to bus: %s", strerror(-r)); goto finish;