diff --git a/src/busctl/busctl.c b/src/busctl/busctl.c index 557a4571f2..cec300e0a0 100644 --- a/src/busctl/busctl.c +++ b/src/busctl/busctl.c @@ -6,6 +6,7 @@ #include "sd-json.h" #include "alloc-util.h" +#include "bitfield.h" #include "build.h" #include "bus-dump.h" #include "bus-internal.h" @@ -23,6 +24,7 @@ #include "glyph-util.h" #include "json-util.h" #include "log.h" +#include "logarithm.h" #include "main-func.h" #include "memstream-util.h" #include "os-util.h" @@ -791,10 +793,6 @@ static int member_compare_func(const Member *x, const Member *y) { return strcmp_ptr(x->signature, y->signature); } -static int member_compare_funcp(Member * const *a, Member * const *b) { - return member_compare_func(*a, *b); -} - static Member* member_free(Member *m) { if (!m) return NULL; @@ -808,11 +806,6 @@ static Member* member_free(Member *m) { } DEFINE_TRIVIAL_CLEANUP_FUNC(Member*, member_free); -static Set* member_set_free(Set *s) { - return set_free_with_destructor(s, member_free); -} -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 = NULL; Set *members = ASSERT_PTR(userdata); @@ -829,18 +822,17 @@ static int on_interface(const char *interface, uint64_t flags, void *userdata) { .flags = flags, }; - r = free_and_strdup(&m->interface, interface); + r = strdup_to(&m->interface, interface); if (r < 0) return log_oom(); - r = set_put(members, m); + r = set_consume(members, TAKE_PTR(m)); + if (r < 0) + return log_oom(); if (r == 0) return log_error_errno(SYNTHETIC_ERRNO(EEXIST), "Invalid introspection data: duplicate interface '%s'.", interface); - if (r < 0) - return log_oom(); - m = NULL; return 0; } @@ -861,30 +853,29 @@ static int on_method(const char *interface, const char *name, const char *signat .flags = flags, }; - r = free_and_strdup(&m->interface, interface); + r = strdup_to(&m->interface, interface); if (r < 0) return log_oom(); - r = free_and_strdup(&m->name, name); + r = strdup_to(&m->name, name); if (r < 0) return log_oom(); - r = free_and_strdup(&m->signature, signature); + r = strdup_to(&m->signature, signature); if (r < 0) return log_oom(); - r = free_and_strdup(&m->result, result); + r = strdup_to(&m->result, result); if (r < 0) return log_oom(); - r = set_put(members, m); + r = set_consume(members, TAKE_PTR(m)); + if (r < 0) + return log_oom(); if (r == 0) return log_error_errno(SYNTHETIC_ERRNO(EEXIST), "Invalid introspection data: duplicate method '%s' on interface '%s'.", name, interface); - if (r < 0) - return log_oom(); - m = NULL; return 0; } @@ -905,26 +896,25 @@ static int on_signal(const char *interface, const char *name, const char *signat .flags = flags, }; - r = free_and_strdup(&m->interface, interface); + r = strdup_to(&m->interface, interface); if (r < 0) return log_oom(); - r = free_and_strdup(&m->name, name); + r = strdup_to(&m->name, name); if (r < 0) return log_oom(); - r = free_and_strdup(&m->signature, signature); + r = strdup_to(&m->signature, signature); if (r < 0) return log_oom(); - r = set_put(members, m); + r = set_consume(members, TAKE_PTR(m)); + if (r < 0) + return log_oom(); if (r == 0) return log_error_errno(SYNTHETIC_ERRNO(EEXIST), "Invalid introspection data: duplicate signal '%s' on interface '%s'.", name, interface); - if (r < 0) - return log_oom(); - m = NULL; return 0; } @@ -946,30 +936,60 @@ static int on_property(const char *interface, const char *name, const char *sign .writable = writable, }; - r = free_and_strdup(&m->interface, interface); + r = strdup_to(&m->interface, interface); if (r < 0) return log_oom(); - r = free_and_strdup(&m->name, name); + r = strdup_to(&m->name, name); if (r < 0) return log_oom(); - r = free_and_strdup(&m->signature, signature); + r = strdup_to(&m->signature, signature); if (r < 0) return log_oom(); - r = set_put(members, m); + r = set_consume(members, TAKE_PTR(m)); + if (r < 0) + return log_oom(); if (r == 0) return log_error_errno(SYNTHETIC_ERRNO(EEXIST), "Invalid introspection data: duplicate property '%s' on interface '%s'.", name, interface); - if (r < 0) - return log_oom(); - m = NULL; return 0; } -DEFINE_PRIVATE_HASH_OPS(member_hash_ops, Member, member_hash_func, member_compare_func); +DEFINE_PRIVATE_HASH_OPS_WITH_KEY_DESTRUCTOR( + member_hash_ops, + Member, + member_hash_func, + member_compare_func, + member_free); + +static int members_flags_to_string(const Member *m, char **ret) { + static const char *map[] = { + [LOG2U(SD_BUS_VTABLE_DEPRECATED)] = "deprecated", + [LOG2U(SD_BUS_VTABLE_METHOD_NO_REPLY)] = "no-reply", + [LOG2U(SD_BUS_VTABLE_PROPERTY_CONST)] = "const", + [LOG2U(SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE)] = "emits-change", + [LOG2U(SD_BUS_VTABLE_PROPERTY_EMITS_INVALIDATION)] = "emits-invalidation", + }; + + assert(m); + assert(ret); + + _cleanup_free_ char *str = NULL; + for (size_t i = 0; i < ELEMENTSOF(map); i++) + if (BIT_SET(m->flags, i) && map[i]) + if (!strextend_with_separator(&str, " ", map[i])) + return -ENOMEM; + + if (m->writable) + if (!strextend_with_separator(&str, " ", "writable")) + return -ENOMEM; + + *ret = TAKE_PTR(str); + return 0; +} static int introspect(int argc, char **argv, void *userdata) { static const XMLIntrospectOps ops = { @@ -982,8 +1002,7 @@ static int introspect(int argc, char **argv, void *userdata) { _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL; _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply_xml = NULL; _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL; - _cleanup_(member_set_freep) Set *members = NULL; - unsigned name_width, type_width, signature_width, result_width; + _cleanup_set_free_ Set *members = NULL; Member *m; const char *xml; int r; @@ -1109,55 +1128,26 @@ static int introspect(int argc, char **argv, void *userdata) { return bus_log_parse_error(r); } - name_width = strlen("NAME"); - type_width = strlen("TYPE"); - signature_width = strlen("SIGNATURE"); - result_width = strlen("RESULT/VALUE"); + size_t n; + _cleanup_free_ Member **sorted = NULL; + r = set_dump_sorted(members, (void***) &sorted, &n); + if (r < 0) + return log_oom(); - Member **sorted = newa(Member*, set_size(members)); - size_t k = 0; + _cleanup_(table_unrefp) Table *table = table_new("name", "type", "signature", "result/value", "flags"); + if (!table) + return log_oom(); - SET_FOREACH(m, members) { - if (argv[3] && !streq(argv[3], m->interface)) - continue; + if (arg_full) + table_set_width(table, 0); + (void) table_set_maximum_width(table, table_get_cell(table, 0, 3), arg_full ? 100 : 40); + table_set_header(table, arg_legend); + table_set_ersatz_string(table, TABLE_ERSATZ_DASH); - 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)); - if (m->value) - result_width = MAX(result_width, strlen(m->value)); - - sorted[k++] = m; - } - - if (result_width > 40 && arg_full <= 0) - result_width = 40; - - typesafe_qsort(sorted, k, member_compare_funcp); - - pager_open(arg_pager_flags); - - 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"); - - for (size_t j = 0; j < k; j++) { - _cleanup_free_ char *ellipsized = NULL; - const char *rv; + FOREACH_ARRAY(p, sorted, n) { bool is_interface; - m = sorted[j]; + m = *p; if (argv[3] && !streq(argv[3], m->interface)) continue; @@ -1167,32 +1157,38 @@ static int introspect(int argc, char **argv, void *userdata) { if (argv[3] && is_interface) continue; - if (m->value) { - ellipsized = ellipsize(m->value, result_width, 100); - if (!ellipsized) - return log_oom(); + if (is_interface) + r = table_add_many( + table, + TABLE_STRING, m->interface, + TABLE_SET_COLOR, ansi_highlight()); + else + r = table_add_cell_stringf( + table, /* ret_cell = */ NULL, ".%s", m->name); + if (r < 0) + return table_log_add_error(r); - rv = ellipsized; - } else - rv = empty_to_dash(m->result); + _cleanup_free_ char *flags = NULL; + r = members_flags_to_string(m, &flags); + if (r < 0) + return log_oom(); - printf("%s%s%-*s%s %-*s %-*s %-*s%s%s%s%s%s%s\n", - is_interface ? ansi_highlight() : "", - is_interface ? "" : ".", - - !is_interface + (int) name_width, - empty_to_dash(streq_ptr(m->type, "interface") ? m->interface : m->name), - is_interface ? ansi_normal() : "", - (int) type_width, empty_to_dash(m->type), - (int) signature_width, empty_to_dash(m->signature), - (int) result_width, rv, - (m->flags & SD_BUS_VTABLE_DEPRECATED) ? " deprecated" : (m->flags || m->writable ? "" : " -"), - (m->flags & SD_BUS_VTABLE_METHOD_NO_REPLY) ? " no-reply" : "", - (m->flags & SD_BUS_VTABLE_PROPERTY_CONST) ? " const" : "", - (m->flags & SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE) ? " emits-change" : "", - (m->flags & SD_BUS_VTABLE_PROPERTY_EMITS_INVALIDATION) ? " emits-invalidation" : "", - m->writable ? " writable" : ""); + r = table_add_many( + table, + TABLE_STRING, m->type, + TABLE_STRING, m->signature, + TABLE_STRING, m->value ?: m->result, + TABLE_STRING, flags); + if (r < 0) + return table_log_add_error(r); } + pager_open(arg_pager_flags); + + r = table_print(table, NULL); + if (r < 0) + return table_log_print_error(r); + return 0; }