diff --git a/src/basic/hashmap.c b/src/basic/hashmap.c index 3093156748..ac1c732978 100644 --- a/src/basic/hashmap.c +++ b/src/basic/hashmap.c @@ -21,6 +21,7 @@ #include "random-util.h" #include "set.h" #include "siphash24.h" +#include "sort-util.h" #include "string-util.h" #include "strv.h" @@ -2106,3 +2107,51 @@ bool set_fnmatch(Set *include_patterns, Set *exclude_patterns, const char *needl return set_fnmatch_one(include_patterns, needle); } + +static int hashmap_entry_compare( + struct hashmap_base_entry * const *a, + struct hashmap_base_entry * const *b, + compare_func_t compare) { + + assert(a && *a); + assert(b && *b); + assert(compare); + + return compare((*a)->key, (*b)->key); +} + +int _hashmap_dump_sorted(HashmapBase *h, void ***ret, size_t *ret_n) { + _cleanup_free_ struct hashmap_base_entry **entries = NULL; + Iterator iter; + unsigned idx; + size_t n = 0; + + assert(ret); + + if (_hashmap_size(h) == 0) { + *ret = NULL; + if (ret_n) + *ret_n = 0; + return 0; + } + + entries = new(struct hashmap_base_entry*, _hashmap_size(h)); + if (!entries) + return -ENOMEM; + + HASHMAP_FOREACH_IDX(idx, h, iter) + entries[n++] = bucket_at(h, idx); + + assert(n == _hashmap_size(h)); + + typesafe_qsort_r(entries, n, hashmap_entry_compare, h->hash_ops->compare); + + /* Reuse the array. */ + FOREACH_ARRAY(e, entries, n) + *e = entry_value(h, *e); + + *ret = (void**) TAKE_PTR(entries); + if (ret_n) + *ret_n = n; + return 0; +} diff --git a/src/basic/hashmap.h b/src/basic/hashmap.h index 68d9b81cf2..ad85093d69 100644 --- a/src/basic/hashmap.h +++ b/src/basic/hashmap.h @@ -398,6 +398,17 @@ static inline char** ordered_hashmap_get_strv(OrderedHashmap *h) { return _hashmap_get_strv(HASHMAP_BASE(h)); } +int _hashmap_dump_sorted(HashmapBase *h, void ***ret, size_t *ret_n); +static inline int hashmap_dump_sorted(Hashmap *h, void ***ret, size_t *ret_n) { + return _hashmap_dump_sorted(HASHMAP_BASE(h), ret, ret_n); +} +static inline int ordered_hashmap_dump_sorted(OrderedHashmap *h, void ***ret, size_t *ret_n) { + return _hashmap_dump_sorted(HASHMAP_BASE(h), ret, ret_n); +} +static inline int set_dump_sorted(Set *h, void ***ret, size_t *ret_n) { + return _hashmap_dump_sorted(HASHMAP_BASE(h), ret, ret_n); +} + /* * Hashmaps are iterated in unpredictable order. * OrderedHashmaps are an exception to this. They are iterated in the order diff --git a/src/busctl/busctl.c b/src/busctl/busctl.c index 748902093d..72eed36335 100644 --- a/src/busctl/busctl.c +++ b/src/busctl/busctl.c @@ -1671,15 +1671,26 @@ static int message_append_cmdline(sd_bus_message *m, const char *signature, char static int json_transform_one(sd_bus_message *m, JsonVariant **ret); -static int json_transform_array_or_struct(sd_bus_message *m, JsonVariant **ret) { - JsonVariant **elements = NULL; - size_t n_elements = 0; +static int json_transform_and_append(sd_bus_message *m, JsonVariant **ret) { + _cleanup_(json_variant_unrefp) JsonVariant *element = NULL; int r; assert(m); assert(ret); - CLEANUP_ARRAY(elements, n_elements, json_variant_unref_many); + r = json_transform_one(m, &element); + if (r < 0) + return r; + + return json_variant_append_array(ret, element); +} + +static int json_transform_array_or_struct(sd_bus_message *m, JsonVariant **ret) { + _cleanup_(json_variant_unrefp) JsonVariant *array = NULL; + int r; + + assert(m); + assert(ret); for (;;) { r = sd_bus_message_at_end(m, false); @@ -1688,17 +1699,16 @@ static int json_transform_array_or_struct(sd_bus_message *m, JsonVariant **ret) if (r > 0) break; - if (!GREEDY_REALLOC(elements, n_elements + 1)) - return log_oom(); - - r = json_transform_one(m, elements + n_elements); + r = json_transform_and_append(m, &array); if (r < 0) return r; - - n_elements++; } - return json_variant_new_array(ret, elements, n_elements); + if (!array) + return json_variant_new_array(ret, NULL, 0); + + *ret = TAKE_PTR(array); + return 0; } static int json_transform_variant(sd_bus_message *m, const char *contents, JsonVariant **ret) { @@ -1722,15 +1732,12 @@ static int json_transform_variant(sd_bus_message *m, const char *contents, JsonV } static int json_transform_dict_array(sd_bus_message *m, JsonVariant **ret) { - JsonVariant **elements = NULL; - size_t n_elements = 0; + _cleanup_(json_variant_unrefp) JsonVariant *array = NULL; int r; assert(m); assert(ret); - CLEANUP_ARRAY(elements, n_elements, json_variant_unref_many); - for (;;) { const char *contents; char type; @@ -1747,31 +1754,28 @@ static int json_transform_dict_array(sd_bus_message *m, JsonVariant **ret) { assert(type == 'e'); - if (!GREEDY_REALLOC(elements, n_elements + 2)) - return log_oom(); - r = sd_bus_message_enter_container(m, type, contents); if (r < 0) return bus_log_parse_error(r); - r = json_transform_one(m, elements + n_elements); + r = json_transform_and_append(m, &array); if (r < 0) return r; - n_elements++; - - r = json_transform_one(m, elements + n_elements); + r = json_transform_and_append(m, &array); if (r < 0) return r; - n_elements++; - r = sd_bus_message_exit_container(m); if (r < 0) return bus_log_parse_error(r); } - return json_variant_new_object(ret, elements, n_elements); + if (!array) + return json_variant_new_array(ret, NULL, 0); + + *ret = TAKE_PTR(array); + return 0; } static int json_transform_one(sd_bus_message *m, JsonVariant **ret) { diff --git a/src/network/networkd-json.c b/src/network/networkd-json.c index c32642205e..b9e119ceeb 100644 --- a/src/network/networkd-json.c +++ b/src/network/networkd-json.c @@ -21,8 +21,14 @@ #include "user-util.h" #include "wifi-util.h" +static int json_append_one(JsonVariant **v, const char *name, JsonVariant *w) { + assert(v); + assert(name); + + return json_append(v, JSON_BUILD_OBJECT(JSON_BUILD_PAIR_VARIANT_NON_NULL(name, w))); +} + static int address_build_json(Address *address, JsonVariant **ret) { - _cleanup_(json_variant_unrefp) JsonVariant *v = NULL; _cleanup_free_ char *scope = NULL, *flags = NULL, *state = NULL; int r; @@ -41,7 +47,7 @@ static int address_build_json(Address *address, JsonVariant **ret) { if (r < 0) return r; - r = json_build(&v, JSON_BUILD_OBJECT( + return json_build(ret, JSON_BUILD_OBJECT( JSON_BUILD_PAIR_INTEGER("Family", address->family), JSON_BUILD_PAIR_IN_ADDR("Address", &address->in_addr, address->family), JSON_BUILD_PAIR_IN_ADDR_NON_NULL("Peer", &address->in_addr_peer, address->family), @@ -59,44 +65,31 @@ static int address_build_json(Address *address, JsonVariant **ret) { JSON_BUILD_PAIR_STRING("ConfigSource", network_config_source_to_string(address->source)), JSON_BUILD_PAIR_STRING("ConfigState", state), JSON_BUILD_PAIR_IN_ADDR_NON_NULL("ConfigProvider", &address->provider, address->family))); - if (r < 0) - return r; - - *ret = TAKE_PTR(v); - return 0; } -static int addresses_build_json(Set *addresses, JsonVariant **ret) { - JsonVariant **elements = NULL; +static int addresses_append_json(Set *addresses, JsonVariant **v) { + _cleanup_(json_variant_unrefp) JsonVariant *array = NULL; Address *address; - size_t n = 0; int r; - assert(ret); - - CLEANUP_ARRAY(elements, n, json_variant_unref_many); - - if (set_isempty(addresses)) { - *ret = NULL; - return 0; - } - - elements = new(JsonVariant*, set_size(addresses)); - if (!elements) - return -ENOMEM; + assert(v); SET_FOREACH(address, addresses) { - r = address_build_json(address, elements + n); + _cleanup_(json_variant_unrefp) JsonVariant *e = NULL; + + r = address_build_json(address, &e); + if (r < 0) + return r; + + r = json_variant_append_array(&array, e); if (r < 0) return r; - n++; } - return json_build(ret, JSON_BUILD_OBJECT(JSON_BUILD_PAIR("Addresses", JSON_BUILD_VARIANT_ARRAY(elements, n)))); + return json_append_one(v, "Addresses", array); } static int neighbor_build_json(Neighbor *n, JsonVariant **ret) { - _cleanup_(json_variant_unrefp) JsonVariant *v = NULL; _cleanup_free_ char *state = NULL; int r; @@ -107,83 +100,64 @@ static int neighbor_build_json(Neighbor *n, JsonVariant **ret) { if (r < 0) return r; - r = json_build(&v, JSON_BUILD_OBJECT( + return json_build(ret, JSON_BUILD_OBJECT( JSON_BUILD_PAIR_INTEGER("Family", n->family), JSON_BUILD_PAIR_IN_ADDR("Destination", &n->in_addr, n->family), JSON_BUILD_PAIR_HW_ADDR("LinkLayerAddress", &n->ll_addr), JSON_BUILD_PAIR_STRING("ConfigSource", network_config_source_to_string(n->source)), JSON_BUILD_PAIR_STRING("ConfigState", state))); - if (r < 0) - return r; - - *ret = TAKE_PTR(v); - return 0; } -static int neighbors_build_json(Set *neighbors, JsonVariant **ret) { - JsonVariant **elements = NULL; +static int neighbors_append_json(Set *neighbors, JsonVariant **v) { + _cleanup_(json_variant_unrefp) JsonVariant *array = NULL; Neighbor *neighbor; - size_t n = 0; int r; - assert(ret); - - CLEANUP_ARRAY(elements, n, json_variant_unref_many); - - if (set_isempty(neighbors)) { - *ret = NULL; - return 0; - } - - elements = new(JsonVariant*, set_size(neighbors)); - if (!elements) - return -ENOMEM; + assert(v); SET_FOREACH(neighbor, neighbors) { - r = neighbor_build_json(neighbor, elements + n); + _cleanup_(json_variant_unrefp) JsonVariant *e = NULL; + + r = neighbor_build_json(neighbor, &e); + if (r < 0) + return r; + + r = json_variant_append_array(&array, e); if (r < 0) return r; - n++; } - return json_build(ret, JSON_BUILD_OBJECT(JSON_BUILD_PAIR("Neighbors", JSON_BUILD_VARIANT_ARRAY(elements, n)))); + return json_append_one(v, "Neighbors", array); } static int nexthop_group_build_json(NextHop *nexthop, JsonVariant **ret) { - JsonVariant **elements = NULL; + _cleanup_(json_variant_unrefp) JsonVariant *array = NULL; struct nexthop_grp *g; - size_t n = 0; int r; assert(nexthop); assert(ret); - CLEANUP_ARRAY(elements, n, json_variant_unref_many); - - if (hashmap_isempty(nexthop->group)) { - *ret = NULL; - return 0; - } - - elements = new(JsonVariant*, hashmap_size(nexthop->group)); - if (!elements) - return -ENOMEM; - HASHMAP_FOREACH(g, nexthop->group) { - r = json_build(elements + n, JSON_BUILD_OBJECT( + _cleanup_(json_variant_unrefp) JsonVariant *e = NULL; + + r = json_build(&e, JSON_BUILD_OBJECT( JSON_BUILD_PAIR_UNSIGNED("ID", g->id), JSON_BUILD_PAIR_UNSIGNED("Weight", g->weight+1))); if (r < 0) return r; - n++; + r = json_variant_append_array(&array, e); + if (r < 0) + return r; } - return json_variant_new_array(ret, elements, n); + *ret = TAKE_PTR(array); + return 0; } static int nexthop_build_json(NextHop *n, JsonVariant **ret) { - _cleanup_(json_variant_unrefp) JsonVariant *v = NULL, *group = NULL; + _cleanup_(json_variant_unrefp) JsonVariant *group = NULL; _cleanup_free_ char *flags = NULL, *protocol = NULL, *state = NULL; int r; @@ -206,7 +180,7 @@ static int nexthop_build_json(NextHop *n, JsonVariant **ret) { if (r < 0) return r; - r = json_build(&v, JSON_BUILD_OBJECT( + return json_build(ret, JSON_BUILD_OBJECT( JSON_BUILD_PAIR_UNSIGNED("ID", n->id), JSON_BUILD_PAIR_IN_ADDR_NON_NULL("Gateway", &n->gw, n->family), JSON_BUILD_PAIR_UNSIGNED("Flags", n->flags), @@ -217,44 +191,31 @@ static int nexthop_build_json(NextHop *n, JsonVariant **ret) { JSON_BUILD_PAIR_VARIANT_NON_NULL("Group", group), JSON_BUILD_PAIR_STRING("ConfigSource", network_config_source_to_string(n->source)), JSON_BUILD_PAIR_STRING("ConfigState", state))); - if (r < 0) - return r; - - *ret = TAKE_PTR(v); - return 0; } -static int nexthops_build_json(Set *nexthops, JsonVariant **ret) { - JsonVariant **elements = NULL; +static int nexthops_append_json(Set *nexthops, JsonVariant **v) { + _cleanup_(json_variant_unrefp) JsonVariant *array = NULL; NextHop *nexthop; - size_t n = 0; int r; - assert(ret); - - CLEANUP_ARRAY(elements, n, json_variant_unref_many); - - if (set_isempty(nexthops)) { - *ret = NULL; - return 0; - } - - elements = new(JsonVariant*, set_size(nexthops)); - if (!elements) - return -ENOMEM; + assert(v); SET_FOREACH(nexthop, nexthops) { - r = nexthop_build_json(nexthop, elements + n); + _cleanup_(json_variant_unrefp) JsonVariant *e = NULL; + + r = nexthop_build_json(nexthop, &e); + if (r < 0) + return r; + + r = json_variant_append_array(&array, e); if (r < 0) return r; - n++; } - return json_build(ret, JSON_BUILD_OBJECT(JSON_BUILD_PAIR("NextHops", JSON_BUILD_VARIANT_ARRAY(elements, n)))); + return json_append_one(v, "NextHops", array); } static int route_build_json(Route *route, JsonVariant **ret) { - _cleanup_(json_variant_unrefp) JsonVariant *v = NULL; _cleanup_free_ char *scope = NULL, *protocol = NULL, *table = NULL, *flags = NULL, *state = NULL; Manager *manager; int r; @@ -286,7 +247,7 @@ static int route_build_json(Route *route, JsonVariant **ret) { if (r < 0) return r; - r = json_build(&v, JSON_BUILD_OBJECT( + return json_build(ret, JSON_BUILD_OBJECT( JSON_BUILD_PAIR_INTEGER("Family", route->family), JSON_BUILD_PAIR_IN_ADDR("Destination", &route->dst, route->family), JSON_BUILD_PAIR_UNSIGNED("DestinationPrefixLength", route->dst_prefixlen), @@ -312,44 +273,31 @@ static int route_build_json(Route *route, JsonVariant **ret) { JSON_BUILD_PAIR_STRING("ConfigSource", network_config_source_to_string(route->source)), JSON_BUILD_PAIR_STRING("ConfigState", state), JSON_BUILD_PAIR_IN_ADDR_NON_NULL("ConfigProvider", &route->provider, route->family))); - if (r < 0) - return r; - - *ret = TAKE_PTR(v); - return 0; } -static int routes_build_json(Set *routes, JsonVariant **ret) { - JsonVariant **elements = NULL; +static int routes_append_json(Set *routes, JsonVariant **v) { + _cleanup_(json_variant_unrefp) JsonVariant *array = NULL; Route *route; - size_t n = 0; int r; - assert(ret); - - CLEANUP_ARRAY(elements, n, json_variant_unref_many); - - if (set_isempty(routes)) { - *ret = NULL; - return 0; - } - - elements = new(JsonVariant*, set_size(routes)); - if (!elements) - return -ENOMEM; + assert(v); SET_FOREACH(route, routes) { - r = route_build_json(route, elements + n); + _cleanup_(json_variant_unrefp) JsonVariant *e = NULL; + + r = route_build_json(route, &e); + if (r < 0) + return r; + + r = json_variant_append_array(&array, e); if (r < 0) return r; - n++; } - return json_build(ret, JSON_BUILD_OBJECT(JSON_BUILD_PAIR("Routes", JSON_BUILD_VARIANT_ARRAY(elements, n)))); + return json_append_one(v, "Routes", array); } static int routing_policy_rule_build_json(RoutingPolicyRule *rule, JsonVariant **ret) { - _cleanup_(json_variant_unrefp) JsonVariant *v = NULL; _cleanup_free_ char *table = NULL, *protocol = NULL, *state = NULL; int r; @@ -369,7 +317,7 @@ static int routing_policy_rule_build_json(RoutingPolicyRule *rule, JsonVariant * if (r < 0) return r; - r = json_build(&v, JSON_BUILD_OBJECT( + return json_build(ret, JSON_BUILD_OBJECT( JSON_BUILD_PAIR_INTEGER("Family", rule->family), JSON_BUILD_PAIR_IN_ADDR_NON_NULL("FromPrefix", &rule->from, rule->family), JSON_BUILD_PAIR_CONDITION(in_addr_is_set(rule->family, &rule->from), @@ -404,51 +352,37 @@ static int routing_policy_rule_build_json(RoutingPolicyRule *rule, JsonVariant * JSON_BUILD_PAIR_STRING_NON_EMPTY("OutgoingInterface", rule->oif), JSON_BUILD_PAIR_STRING("ConfigSource", network_config_source_to_string(rule->source)), JSON_BUILD_PAIR_STRING("ConfigState", state))); - if (r < 0) - return r; - - *ret = TAKE_PTR(v); - return 0; } -static int routing_policy_rules_build_json(Set *rules, JsonVariant **ret) { - JsonVariant **elements = NULL; +static int routing_policy_rules_append_json(Set *rules, JsonVariant **v) { + _cleanup_(json_variant_unrefp) JsonVariant *array = NULL; RoutingPolicyRule *rule; - size_t n = 0; int r; - assert(ret); - - CLEANUP_ARRAY(elements, n, json_variant_unref_many); - - if (set_isempty(rules)) { - *ret = NULL; - return 0; - } - - elements = new(JsonVariant*, set_size(rules)); - if (!elements) - return -ENOMEM; + assert(v); SET_FOREACH(rule, rules) { - r = routing_policy_rule_build_json(rule, elements + n); + _cleanup_(json_variant_unrefp) JsonVariant *e = NULL; + + r = routing_policy_rule_build_json(rule, &e); + if (r < 0) + return r; + + r = json_variant_append_array(&array, e); if (r < 0) return r; - n++; } - return json_build(ret, JSON_BUILD_OBJECT(JSON_BUILD_PAIR("RoutingPolicyRules", JSON_BUILD_VARIANT_ARRAY(elements, n)))); + return json_append_one(v, "RoutingPolicyRules", array); } -static int network_build_json(Network *network, JsonVariant **ret) { - assert(ret); +static int network_append_json(Network *network, JsonVariant **v) { + assert(v); - if (!network) { - *ret = NULL; + if (!network) return 0; - } - return json_build(ret, JSON_BUILD_OBJECT( + return json_append(v, JSON_BUILD_OBJECT( JSON_BUILD_PAIR_STRING("NetworkFile", network->filename), JSON_BUILD_PAIR_STRV("NetworkFileDropins", network->dropins), JSON_BUILD_PAIR_BOOLEAN("RequiredForOnline", network->required_for_online), @@ -461,17 +395,15 @@ static int network_build_json(Network *network, JsonVariant **ret) { activation_policy_to_string(network->activation_policy)))); } -static int device_build_json(sd_device *device, JsonVariant **ret) { +static int device_append_json(sd_device *device, JsonVariant **v) { _cleanup_strv_free_ char **link_dropins = NULL; const char *link = NULL, *path = NULL, *vendor = NULL, *model = NULL, *joined; int r; - assert(ret); + assert(v); - if (!device) { - *ret = NULL; + if (!device) return 0; - } (void) sd_device_get_property_value(device, "ID_NET_LINK_FILE", &link); @@ -489,7 +421,7 @@ static int device_build_json(sd_device *device, JsonVariant **ret) { if (sd_device_get_property_value(device, "ID_MODEL_FROM_DATABASE", &model) < 0) (void) sd_device_get_property_value(device, "ID_MODEL", &model); - return json_build(ret, JSON_BUILD_OBJECT( + return json_append(v, JSON_BUILD_OBJECT( JSON_BUILD_PAIR_STRING_NON_EMPTY("LinkFile", link), JSON_BUILD_PAIR_STRV_NON_EMPTY("LinkFileDropins", link_dropins), JSON_BUILD_PAIR_STRING_NON_EMPTY("Path", path), @@ -497,18 +429,16 @@ static int device_build_json(sd_device *device, JsonVariant **ret) { JSON_BUILD_PAIR_STRING_NON_EMPTY("Model", model))); } -static int dns_build_json_one(Link *link, const struct in_addr_full *a, NetworkConfigSource s, const union in_addr_union *p, JsonVariant **ret) { +static int dns_append_json_one(Link *link, const struct in_addr_full *a, NetworkConfigSource s, const union in_addr_union *p, JsonVariant **array) { _cleanup_(json_variant_unrefp) JsonVariant *v = NULL; int r; assert(link); assert(a); - assert(ret); + assert(array); - if (a->ifindex != 0 && a->ifindex != link->ifindex) { - *ret = NULL; + if (a->ifindex != 0 && a->ifindex != link->ifindex) return 0; - } r = json_build(&v, JSON_BUILD_OBJECT( JSON_BUILD_PAIR_INTEGER("Family", a->family), @@ -521,46 +451,30 @@ static int dns_build_json_one(Link *link, const struct in_addr_full *a, NetworkC if (r < 0) return r; - *ret = TAKE_PTR(v); - return 1; + return json_variant_append_array(array, v); } -static int dns_build_json(Link *link, JsonVariant **ret) { - JsonVariant **elements = NULL; - size_t n = 0; +static int dns_append_json(Link *link, JsonVariant **v) { + _cleanup_(json_variant_unrefp) JsonVariant *array = NULL; int r; assert(link); - assert(ret); + assert(v); - CLEANUP_ARRAY(elements, n, json_variant_unref_many); - - if (!link->network) { - *ret = NULL; + if (!link->network) return 0; - } - if (link->n_dns != UINT_MAX) { + if (link->n_dns != UINT_MAX) for (unsigned i = 0; i < link->n_dns; i++) { - if (!GREEDY_REALLOC(elements, n + 1)) - return -ENOMEM; - - r = dns_build_json_one(link, link->dns[i], NETWORK_CONFIG_SOURCE_RUNTIME, NULL, elements + n); + r = dns_append_json_one(link, link->dns[i], NETWORK_CONFIG_SOURCE_RUNTIME, NULL, &array); if (r < 0) return r; - if (r > 0) - n++; } - } else { + else { for (unsigned i = 0; i < link->network->n_dns; i++) { - if (!GREEDY_REALLOC(elements, n + 1)) - return -ENOMEM; - - r = dns_build_json_one(link, link->network->dns[i], NETWORK_CONFIG_SOURCE_STATIC, NULL, elements + n); + r = dns_append_json_one(link, link->network->dns[i], NETWORK_CONFIG_SOURCE_STATIC, NULL, &array); if (r < 0) return r; - if (r > 0) - n++; } if (link->dhcp_lease && link->network->dhcp_use_dns) { @@ -574,18 +488,13 @@ static int dns_build_json(Link *link, JsonVariant **ret) { n_dns = sd_dhcp_lease_get_dns(link->dhcp_lease, &dns); for (int i = 0; i < n_dns; i++) { - if (!GREEDY_REALLOC(elements, n + 1)) - return -ENOMEM; - - r = dns_build_json_one(link, - &(struct in_addr_full) { .family = AF_INET, .address.in = dns[i], }, - NETWORK_CONFIG_SOURCE_DHCP4, - &s, - elements + n); + r = dns_append_json_one(link, + &(struct in_addr_full) { .family = AF_INET, .address.in = dns[i], }, + NETWORK_CONFIG_SOURCE_DHCP4, + &s, + &array); if (r < 0) return r; - if (r > 0) - n++; } } @@ -600,18 +509,13 @@ static int dns_build_json(Link *link, JsonVariant **ret) { n_dns = sd_dhcp6_lease_get_dns(link->dhcp6_lease, &dns); for (int i = 0; i < n_dns; i++) { - if (!GREEDY_REALLOC(elements, n + 1)) - return -ENOMEM; - - r = dns_build_json_one(link, - &(struct in_addr_full) { .family = AF_INET6, .address.in6 = dns[i], }, - NETWORK_CONFIG_SOURCE_DHCP6, - &s, - elements + n); + r = dns_append_json_one(link, + &(struct in_addr_full) { .family = AF_INET6, .address.in6 = dns[i], }, + NETWORK_CONFIG_SOURCE_DHCP6, + &s, + &array); if (r < 0) return r; - if (r > 0) - n++; } } @@ -619,90 +523,83 @@ static int dns_build_json(Link *link, JsonVariant **ret) { NDiscRDNSS *a; SET_FOREACH(a, link->ndisc_rdnss) { - if (!GREEDY_REALLOC(elements, n + 1)) - return -ENOMEM; - - r = dns_build_json_one(link, - &(struct in_addr_full) { .family = AF_INET6, .address.in6 = a->address, }, - NETWORK_CONFIG_SOURCE_NDISC, - &(union in_addr_union) { .in6 = a->router }, - elements + n); + r = dns_append_json_one(link, + &(struct in_addr_full) { .family = AF_INET6, .address.in6 = a->address, }, + NETWORK_CONFIG_SOURCE_NDISC, + &(union in_addr_union) { .in6 = a->router }, + &array); if (r < 0) return r; - if (r > 0) - n++; } } } - if (n == 0) { - *ret = NULL; - return 0; - } - - return json_build(ret, JSON_BUILD_OBJECT(JSON_BUILD_PAIR("DNS", JSON_BUILD_VARIANT_ARRAY(elements, n)))); + return json_append_one(v, "DNS", array); } -static int server_build_json_one_addr(int family, const union in_addr_union *a, NetworkConfigSource s, const union in_addr_union *p, JsonVariant **ret) { +static int server_append_json_one_addr(int family, const union in_addr_union *a, NetworkConfigSource s, const union in_addr_union *p, JsonVariant **array) { + _cleanup_(json_variant_unrefp) JsonVariant *v = NULL; + int r; + assert(IN_SET(family, AF_INET, AF_INET6)); assert(a); - assert(ret); + assert(array); - return json_build(ret, JSON_BUILD_OBJECT( + r = json_build(&v, JSON_BUILD_OBJECT( JSON_BUILD_PAIR_INTEGER("Family", family), JSON_BUILD_PAIR_IN_ADDR("Address", a, family), JSON_BUILD_PAIR_STRING("ConfigSource", network_config_source_to_string(s)), JSON_BUILD_PAIR_IN_ADDR_NON_NULL("ConfigProvider", p, family))); + if (r < 0) + return r; + + return json_variant_append_array(array, v); } -static int server_build_json_one_fqdn(int family, const char *fqdn, NetworkConfigSource s, const union in_addr_union *p, JsonVariant **ret) { +static int server_append_json_one_fqdn(int family, const char *fqdn, NetworkConfigSource s, const union in_addr_union *p, JsonVariant **array) { + _cleanup_(json_variant_unrefp) JsonVariant *v = NULL; + int r; + assert(IN_SET(family, AF_UNSPEC, AF_INET, AF_INET6)); assert(fqdn); - assert(ret); + assert(array); - return json_build(ret, JSON_BUILD_OBJECT( + r = json_build(&v, JSON_BUILD_OBJECT( JSON_BUILD_PAIR_STRING("Server", fqdn), JSON_BUILD_PAIR_STRING("ConfigSource", network_config_source_to_string(s)), JSON_BUILD_PAIR_IN_ADDR_NON_NULL("ConfigProvider", p, family))); + if (r < 0) + return r; + + return json_variant_append_array(array, v); } -static int server_build_json_one_string(const char *str, NetworkConfigSource s, JsonVariant **ret) { +static int server_append_json_one_string(const char *str, NetworkConfigSource s, JsonVariant **array) { union in_addr_union a; int family; assert(str); - assert(ret); if (in_addr_from_string_auto(str, &family, &a) >= 0) - return server_build_json_one_addr(family, &a, s, NULL, ret); + return server_append_json_one_addr(family, &a, s, NULL, array); - return server_build_json_one_fqdn(AF_UNSPEC, str, s, NULL, ret); + return server_append_json_one_fqdn(AF_UNSPEC, str, s, NULL, array); } -static int ntp_build_json(Link *link, JsonVariant **ret) { - JsonVariant **elements = NULL; - size_t n = 0; +static int ntp_append_json(Link *link, JsonVariant **v) { + _cleanup_(json_variant_unrefp) JsonVariant *array = NULL; int r; assert(link); - assert(ret); + assert(v); - CLEANUP_ARRAY(elements, n, json_variant_unref_many); - - if (!link->network) { - *ret = NULL; + if (!link->network) return 0; - } STRV_FOREACH(p, link->ntp ?: link->network->ntp) { - if (!GREEDY_REALLOC(elements, n + 1)) - return -ENOMEM; - - r = server_build_json_one_string(*p, NETWORK_CONFIG_SOURCE_RUNTIME, elements + n); + r = server_append_json_one_string(*p, NETWORK_CONFIG_SOURCE_RUNTIME, &array); if (r < 0) return r; - - n++; } if (!link->ntp) { @@ -717,18 +614,13 @@ static int ntp_build_json(Link *link, JsonVariant **ret) { n_ntp = sd_dhcp_lease_get_ntp(link->dhcp_lease, &ntp); for (int i = 0; i < n_ntp; i++) { - if (!GREEDY_REALLOC(elements, n + 1)) - return -ENOMEM; - - r = server_build_json_one_addr(AF_INET, - &(union in_addr_union) { .in = ntp[i], }, - NETWORK_CONFIG_SOURCE_DHCP4, - &s, - elements + n); + r = server_append_json_one_addr(AF_INET, + &(union in_addr_union) { .in = ntp[i], }, + NETWORK_CONFIG_SOURCE_DHCP4, + &s, + &array); if (r < 0) return r; - - n++; } } @@ -744,157 +636,107 @@ static int ntp_build_json(Link *link, JsonVariant **ret) { n_ntp = sd_dhcp6_lease_get_ntp_addrs(link->dhcp6_lease, &ntp_addr); for (int i = 0; i < n_ntp; i++) { - if (!GREEDY_REALLOC(elements, n + 1)) - return -ENOMEM; - - r = server_build_json_one_addr(AF_INET6, - &(union in_addr_union) { .in6 = ntp_addr[i], }, - NETWORK_CONFIG_SOURCE_DHCP6, - &s, - elements + n); + r = server_append_json_one_addr(AF_INET6, + &(union in_addr_union) { .in6 = ntp_addr[i], }, + NETWORK_CONFIG_SOURCE_DHCP6, + &s, + &array); if (r < 0) return r; - - n++; } n_ntp = sd_dhcp6_lease_get_ntp_fqdn(link->dhcp6_lease, &ntp_fqdn); for (int i = 0; i < n_ntp; i++) { - if (!GREEDY_REALLOC(elements, n + 1)) - return -ENOMEM; - - r = server_build_json_one_fqdn(AF_INET6, - ntp_fqdn[i], - NETWORK_CONFIG_SOURCE_DHCP6, - &s, - elements + n); + r = server_append_json_one_fqdn(AF_INET6, + ntp_fqdn[i], + NETWORK_CONFIG_SOURCE_DHCP6, + &s, + &array); if (r < 0) return r; - - n++; } } } - if (n == 0) { - *ret = NULL; - return 0; - } - - return json_build(ret, JSON_BUILD_OBJECT(JSON_BUILD_PAIR("NTP", JSON_BUILD_VARIANT_ARRAY(elements, n)))); + return json_append_one(v, "NTP", array); } -static int sip_build_json(Link *link, JsonVariant **ret) { +static int sip_append_json(Link *link, JsonVariant **v) { + _cleanup_(json_variant_unrefp) JsonVariant *array = NULL; const struct in_addr *sip; - JsonVariant **elements = NULL; union in_addr_union s; - size_t n = 0; int n_sip, r; assert(link); - assert(ret); + assert(v); - CLEANUP_ARRAY(elements, n, json_variant_unref_many); - - if (!link->network || !link->network->dhcp_use_sip || !link->dhcp_lease) { - *ret = NULL; + if (!link->network || !link->network->dhcp_use_sip || !link->dhcp_lease) return 0; - } n_sip = sd_dhcp_lease_get_sip(link->dhcp_lease, &sip); - if (n_sip <= 0) { - *ret = NULL; + if (n_sip <= 0) return 0; - } r = sd_dhcp_lease_get_server_identifier(link->dhcp_lease, &s.in); if (r < 0) return r; - elements = new(JsonVariant*, n_sip); - if (!elements) - return -ENOMEM; - for (int i = 0; i < n_sip; i++) { - r = server_build_json_one_addr(AF_INET, - &(union in_addr_union) { .in = sip[i], }, - NETWORK_CONFIG_SOURCE_DHCP4, - &s, - elements + n); + r = server_append_json_one_addr(AF_INET, + &(union in_addr_union) { .in = sip[i], }, + NETWORK_CONFIG_SOURCE_DHCP4, + &s, + &array); if (r < 0) return r; - if (r > 0) - n++; } - return json_build(ret, JSON_BUILD_OBJECT(JSON_BUILD_PAIR("SIP", JSON_BUILD_VARIANT_ARRAY(elements, n)))); + return json_append_one(v, "SIP", array); } -static int captive_portal_build_json(Link *link, JsonVariant **ret) { - const char *captive_portal; +static int domain_append_json(int family, const char *domain, NetworkConfigSource s, const union in_addr_union *p, JsonVariant **array) { + _cleanup_(json_variant_unrefp) JsonVariant *v = NULL; int r; - assert(link); - assert(ret); - - r = link_get_captive_portal(link, &captive_portal); - if (r < 0) - return r; - - if (!captive_portal) { - *ret = NULL; - return 0; - } - - return json_build(ret, JSON_BUILD_OBJECT(JSON_BUILD_PAIR_STRING("CaptivePortal", captive_portal))); -} - -static int domain_build_json(int family, const char *domain, NetworkConfigSource s, const union in_addr_union *p, JsonVariant **ret) { assert(IN_SET(family, AF_UNSPEC, AF_INET, AF_INET6)); assert(domain); - assert(ret); + assert(array); - return json_build(ret, JSON_BUILD_OBJECT( + r = json_build(&v, JSON_BUILD_OBJECT( JSON_BUILD_PAIR_STRING("Domain", domain), JSON_BUILD_PAIR_STRING("ConfigSource", network_config_source_to_string(s)), JSON_BUILD_PAIR_IN_ADDR_NON_NULL("ConfigProvider", p, family))); + if (r < 0) + return r; + + return json_variant_append_array(array, v); } -static int domains_build_json(Link *link, bool is_route, JsonVariant **ret) { +static int domains_append_json(Link *link, bool is_route, JsonVariant **v) { + _cleanup_(json_variant_unrefp) JsonVariant *array = NULL; OrderedSet *link_domains, *network_domains; - JsonVariant **elements = NULL; DHCPUseDomains use_domains; union in_addr_union s; char **domains; const char *domain; - size_t n = 0; int r; assert(link); - assert(ret); + assert(v); - CLEANUP_ARRAY(elements, n, json_variant_unref_many); - - if (!link->network) { - *ret = NULL; + if (!link->network) return 0; - } link_domains = is_route ? link->route_domains : link->search_domains; network_domains = is_route ? link->network->route_domains : link->network->search_domains; use_domains = is_route ? DHCP_USE_DOMAINS_ROUTE : DHCP_USE_DOMAINS_YES; ORDERED_SET_FOREACH(domain, link_domains ?: network_domains) { - if (!GREEDY_REALLOC(elements, n + 1)) - return -ENOMEM; - - r = domain_build_json(AF_UNSPEC, domain, - link_domains ? NETWORK_CONFIG_SOURCE_RUNTIME : NETWORK_CONFIG_SOURCE_STATIC, - NULL, elements + n); + r = domain_append_json(AF_UNSPEC, domain, + link_domains ? NETWORK_CONFIG_SOURCE_RUNTIME : NETWORK_CONFIG_SOURCE_STATIC, + NULL, &array); if (r < 0) return r; - - n++; } if (!link_domains) { @@ -905,28 +747,17 @@ static int domains_build_json(Link *link, bool is_route, JsonVariant **ret) { return r; if (sd_dhcp_lease_get_domainname(link->dhcp_lease, &domain) >= 0) { - if (!GREEDY_REALLOC(elements, n + 1)) - return -ENOMEM; - - r = domain_build_json(AF_INET, domain, NETWORK_CONFIG_SOURCE_DHCP4, &s, elements + n); + r = domain_append_json(AF_INET, domain, NETWORK_CONFIG_SOURCE_DHCP4, &s, &array); if (r < 0) return r; - - n++; } - if (sd_dhcp_lease_get_search_domains(link->dhcp_lease, &domains) >= 0) { + if (sd_dhcp_lease_get_search_domains(link->dhcp_lease, &domains) >= 0) STRV_FOREACH(p, domains) { - if (!GREEDY_REALLOC(elements, n + 1)) - return -ENOMEM; - - r = domain_build_json(AF_INET, *p, NETWORK_CONFIG_SOURCE_DHCP4, &s, elements + n); + r = domain_append_json(AF_INET, *p, NETWORK_CONFIG_SOURCE_DHCP4, &s, &array); if (r < 0) return r; - - n++; } - } } if (link->dhcp6_lease && @@ -935,197 +766,176 @@ static int domains_build_json(Link *link, bool is_route, JsonVariant **ret) { if (r < 0) return r; - if (sd_dhcp6_lease_get_domains(link->dhcp6_lease, &domains) >= 0) { + if (sd_dhcp6_lease_get_domains(link->dhcp6_lease, &domains) >= 0) STRV_FOREACH(p, domains) { - if (!GREEDY_REALLOC(elements, n + 1)) - return -ENOMEM; - - r = domain_build_json(AF_INET6, *p, NETWORK_CONFIG_SOURCE_DHCP6, &s, elements + n); + r = domain_append_json(AF_INET6, *p, NETWORK_CONFIG_SOURCE_DHCP6, &s, &array); if (r < 0) return r; - - n++; } - } } if (link->network->ipv6_accept_ra_use_domains == use_domains) { NDiscDNSSL *a; SET_FOREACH(a, link->ndisc_dnssl) { - if (!GREEDY_REALLOC(elements, n + 1)) - return -ENOMEM; - - r = domain_build_json(AF_INET6, NDISC_DNSSL_DOMAIN(a), NETWORK_CONFIG_SOURCE_NDISC, - &(union in_addr_union) { .in6 = a->router }, - elements + n); + r = domain_append_json(AF_INET6, NDISC_DNSSL_DOMAIN(a), NETWORK_CONFIG_SOURCE_NDISC, + &(union in_addr_union) { .in6 = a->router }, + &array); if (r < 0) return r; - - n++; } } } - if (n == 0) { - *ret = NULL; - return 0; - } - - return json_build(ret, JSON_BUILD_OBJECT(JSON_BUILD_PAIR(is_route ? "RouteDomains" : "SearchDomains", - JSON_BUILD_VARIANT_ARRAY(elements, n)))); + return json_append_one(v, is_route ? "RouteDomains" : "SearchDomains", array); } -static int nta_build_json(const char *nta, NetworkConfigSource s, JsonVariant **ret) { - assert(nta); - assert(ret); +static int nta_append_json(const char *nta, NetworkConfigSource s, JsonVariant **array) { + _cleanup_(json_variant_unrefp) JsonVariant *v = NULL; + int r; - return json_build(ret, JSON_BUILD_OBJECT( + assert(nta); + assert(array); + + r = json_build(&v, JSON_BUILD_OBJECT( JSON_BUILD_PAIR_STRING("DNSSECNegativeTrustAnchor", nta), JSON_BUILD_PAIR_STRING("ConfigSource", network_config_source_to_string(s)))); + if (r < 0) + return r; + + return json_variant_append_array(array, v); } -static int ntas_build_json(Link *link, JsonVariant **ret) { - JsonVariant **elements = NULL; +static int ntas_append_json(Link *link, JsonVariant **v) { + _cleanup_(json_variant_unrefp) JsonVariant *array = NULL; const char *nta; - size_t n = 0; int r; assert(link); - assert(ret); + assert(v); - CLEANUP_ARRAY(elements, n, json_variant_unref_many); - - if (!link->network) { - *ret = NULL; + if (!link->network) return 0; - } SET_FOREACH(nta, link->dnssec_negative_trust_anchors ?: link->network->dnssec_negative_trust_anchors) { - if (!GREEDY_REALLOC(elements, n + 1)) - return -ENOMEM; - - r = nta_build_json(nta, + r = nta_append_json(nta, link->dnssec_negative_trust_anchors ? NETWORK_CONFIG_SOURCE_RUNTIME : NETWORK_CONFIG_SOURCE_STATIC, - elements + n); + &array); if (r < 0) return r; - - n++; } - if (n == 0) { - *ret = NULL; - return 0; - } - - return json_build(ret, JSON_BUILD_OBJECT(JSON_BUILD_PAIR("DNSSECNegativeTrustAnchors", - JSON_BUILD_VARIANT_ARRAY(elements, n)))); + return json_append_one(v, "DNSSECNegativeTrustAnchors", array); } -static int dns_misc_build_json(Link *link, JsonVariant **ret) { - JsonVariant **elements = NULL; +static int dns_misc_append_json(Link *link, JsonVariant **v) { + _cleanup_(json_variant_unrefp) JsonVariant *array = NULL; ResolveSupport resolve_support; NetworkConfigSource source; DnsOverTlsMode mode; - size_t n = 0; int t, r; assert(link); - assert(ret); + assert(v); - CLEANUP_ARRAY(elements, n, json_variant_unref_many); - - if (!link->network) { - *ret = NULL; + if (!link->network) return 0; - } resolve_support = link->llmnr >= 0 ? link->llmnr : link->network->llmnr; if (resolve_support >= 0) { + _cleanup_(json_variant_unrefp) JsonVariant *e = NULL; + source = link->llmnr >= 0 ? NETWORK_CONFIG_SOURCE_RUNTIME : NETWORK_CONFIG_SOURCE_STATIC; - if (!GREEDY_REALLOC(elements, n + 1)) - return -ENOMEM; - - r = json_build(elements + n, JSON_BUILD_OBJECT( + r = json_build(&e, JSON_BUILD_OBJECT( JSON_BUILD_PAIR_STRING("LLMNR", resolve_support_to_string(resolve_support)), JSON_BUILD_PAIR_STRING("ConfigSource", network_config_source_to_string(source)))); if (r < 0) return r; - n++; + r = json_variant_append_array(&array, e); + if (r < 0) + return r; } resolve_support = link->mdns >= 0 ? link->mdns : link->network->mdns; if (resolve_support >= 0) { + _cleanup_(json_variant_unrefp) JsonVariant *e = NULL; + source = link->mdns >= 0 ? NETWORK_CONFIG_SOURCE_RUNTIME : NETWORK_CONFIG_SOURCE_STATIC; - if (!GREEDY_REALLOC(elements, n + 1)) - return -ENOMEM; - - r = json_build(elements + n, JSON_BUILD_OBJECT( + r = json_build(&e, JSON_BUILD_OBJECT( JSON_BUILD_PAIR_STRING("MDNS", resolve_support_to_string(resolve_support)), JSON_BUILD_PAIR_STRING("ConfigSource", network_config_source_to_string(source)))); if (r < 0) return r; - n++; + r = json_variant_append_array(&array, e); + if (r < 0) + return r; } t = link->dns_default_route >= 0 ? link->dns_default_route : link->network->dns_default_route; if (t >= 0) { + _cleanup_(json_variant_unrefp) JsonVariant *e = NULL; + source = link->dns_default_route >= 0 ? NETWORK_CONFIG_SOURCE_RUNTIME : NETWORK_CONFIG_SOURCE_STATIC; - if (!GREEDY_REALLOC(elements, n + 1)) - return -ENOMEM; - - r = json_build(elements + n, JSON_BUILD_OBJECT( + r = json_build(&e, JSON_BUILD_OBJECT( JSON_BUILD_PAIR_BOOLEAN("DNSDefaultRoute", t), JSON_BUILD_PAIR_STRING("ConfigSource", network_config_source_to_string(source)))); if (r < 0) return r; - n++; + r = json_variant_append_array(&array, e); + if (r < 0) + return r; } mode = link->dns_over_tls_mode >= 0 ? link->dns_over_tls_mode : link->network->dns_over_tls_mode; if (mode >= 0) { + _cleanup_(json_variant_unrefp) JsonVariant *e = NULL; + source = link->dns_over_tls_mode >= 0 ? NETWORK_CONFIG_SOURCE_RUNTIME : NETWORK_CONFIG_SOURCE_STATIC; - if (!GREEDY_REALLOC(elements, n + 1)) - return -ENOMEM; - - r = json_build(elements + n, JSON_BUILD_OBJECT( + r = json_build(&e, JSON_BUILD_OBJECT( JSON_BUILD_PAIR_STRING("DNSOverTLS", dns_over_tls_mode_to_string(mode)), JSON_BUILD_PAIR_STRING("ConfigSource", network_config_source_to_string(source)))); if (r < 0) return r; - n++; + r = json_variant_append_array(&array, e); + if (r < 0) + return r; } - return json_build(ret, JSON_BUILD_OBJECT(JSON_BUILD_PAIR("DNSSettings", - JSON_BUILD_VARIANT_ARRAY(elements, n)))); + return json_append_one(v, "DNSSettings", array); } -static int dhcp_server_offered_leases_build_json(Link *link, JsonVariant **ret) { - _cleanup_(json_variant_unrefp) JsonVariant *elements = NULL; +static int captive_portal_append_json(Link *link, JsonVariant **v) { + const char *captive_portal; int r; - DHCPLease *lease; assert(link); - assert(ret); + assert(v); - if (!link->dhcp_server) { - *ret = NULL; - return 0; - } + r = link_get_captive_portal(link, &captive_portal); + if (r <= 0) + return r; - if (hashmap_isempty(link->dhcp_server->bound_leases_by_client_id)) { - *ret = NULL; + return json_append(v, JSON_BUILD_OBJECT(JSON_BUILD_PAIR_STRING("CaptivePortal", captive_portal))); +} + +static int dhcp_server_offered_leases_append_json(Link *link, JsonVariant **v) { + _cleanup_(json_variant_unrefp) JsonVariant *array = NULL; + DHCPLease *lease; + int r; + + assert(link); + assert(v); + + if (!link->dhcp_server) return 0; - } HASHMAP_FOREACH(lease, link->dhcp_server->bound_leases_by_client_id) { _cleanup_(json_variant_unrefp) JsonVariant *e = NULL; @@ -1144,31 +954,24 @@ static int dhcp_server_offered_leases_build_json(Link *link, JsonVariant **ret) if (r < 0) return r; - r = json_variant_append_array(&elements, e); + r = json_variant_append_array(&array, e); if (r < 0) return r; } - return json_build(ret, JSON_BUILD_OBJECT(JSON_BUILD_PAIR_VARIANT("Leases", elements))); + return json_append_one(v, "Leases", array); } -static int dhcp_server_static_leases_build_json(Link *link, JsonVariant **ret) { - _cleanup_(json_variant_unrefp) JsonVariant *elements = NULL; - int r; +static int dhcp_server_static_leases_append_json(Link *link, JsonVariant **v) { + _cleanup_(json_variant_unrefp) JsonVariant *array = NULL; DHCPLease *lease; + int r; assert(link); - assert(ret); + assert(v); - if (!link->dhcp_server) { - *ret = NULL; + if (!link->dhcp_server) return 0; - } - - if (hashmap_isempty(link->dhcp_server->static_leases_by_client_id)) { - *ret = NULL; - return 0; - } HASHMAP_FOREACH(lease, link->dhcp_server->static_leases_by_client_id) { _cleanup_(json_variant_unrefp) JsonVariant *e = NULL; @@ -1184,56 +987,44 @@ static int dhcp_server_static_leases_build_json(Link *link, JsonVariant **ret) { if (r < 0) return r; - r = json_variant_append_array(&elements, e); + r = json_variant_append_array(&array, e); if (r < 0) return r; } - return json_build(ret, JSON_BUILD_OBJECT(JSON_BUILD_PAIR_VARIANT("StaticLeases", elements))); + return json_append_one(v, "StaticLeases", array); } -static int dhcp_server_build_json(Link *link, JsonVariant **ret) { - _cleanup_(json_variant_unrefp) JsonVariant *v = NULL, *w = NULL; +static int dhcp_server_append_json(Link *link, JsonVariant **v) { + _cleanup_(json_variant_unrefp) JsonVariant *w = NULL; int r; assert(link); - assert(ret); + assert(v); - if (!link->dhcp_server) { - *ret = NULL; + if (!link->dhcp_server) return 0; - } - r = json_build(&v, + r = json_build(&w, JSON_BUILD_OBJECT( JSON_BUILD_PAIR_UNSIGNED("PoolOffset", link->dhcp_server->pool_offset), JSON_BUILD_PAIR_UNSIGNED("PoolSize", link->dhcp_server->pool_size))); if (r < 0) return r; - r = dhcp_server_offered_leases_build_json(link, &w); + r = dhcp_server_offered_leases_append_json(link, &w); if (r < 0) return r; - r = json_variant_merge(&v, w); + r = dhcp_server_static_leases_append_json(link, &w); if (r < 0) return r; - w = json_variant_unref(w); - - r = dhcp_server_static_leases_build_json(link, &w); - if (r < 0) - return r; - - r = json_variant_merge(&v, w); - if (r < 0) - return r; - - return json_build(ret, JSON_BUILD_OBJECT(JSON_BUILD_PAIR_VARIANT("DHCPServer", v))); + return json_append_one(v, "DHCPServer", w); } int link_build_json(Link *link, JsonVariant **ret) { - _cleanup_(json_variant_unrefp) JsonVariant *v = NULL, *w = NULL; + _cleanup_(json_variant_unrefp) JsonVariant *v = NULL; _cleanup_free_ char *type = NULL, *flags = NULL; int r; @@ -1287,151 +1078,63 @@ int link_build_json(Link *link, JsonVariant **ret) { if (r < 0) return r; - r = network_build_json(link->network, &w); + r = network_append_json(link->network, &v); if (r < 0) return r; - r = json_variant_merge(&v, w); + r = device_append_json(link->dev, &v); if (r < 0) return r; - w = json_variant_unref(w); - - r = device_build_json(link->dev, &w); + r = dns_append_json(link, &v); if (r < 0) return r; - r = json_variant_merge(&v, w); + r = ntp_append_json(link, &v); if (r < 0) return r; - w = json_variant_unref(w); - - r = dns_build_json(link, &w); + r = sip_append_json(link, &v); if (r < 0) return r; - r = json_variant_merge(&v, w); + r = domains_append_json(link, /* is_route = */ false, &v); if (r < 0) return r; - w = json_variant_unref(w); - - r = ntp_build_json(link, &w); + r = domains_append_json(link, /* is_route = */ true, &v); if (r < 0) return r; - r = json_variant_merge(&v, w); + r = ntas_append_json(link, &v); if (r < 0) return r; - w = json_variant_unref(w); - - r = sip_build_json(link, &w); + r = dns_misc_append_json(link, &v); if (r < 0) return r; - r = json_variant_merge(&v, w); + r = captive_portal_append_json(link, &v); if (r < 0) return r; - w = json_variant_unref(w); - - r = captive_portal_build_json(link, &w); + r = addresses_append_json(link->addresses, &v); if (r < 0) return r; - r = json_variant_merge(&v, w); + r = neighbors_append_json(link->neighbors, &v); if (r < 0) return r; - w = json_variant_unref(w); - - r = domains_build_json(link, /* is_route = */ false, &w); + r = nexthops_append_json(link->nexthops, &v); if (r < 0) return r; - r = json_variant_merge(&v, w); + r = routes_append_json(link->routes, &v); if (r < 0) return r; - w = json_variant_unref(w); - - r = domains_build_json(link, /* is_route = */ true, &w); - if (r < 0) - return r; - - r = json_variant_merge(&v, w); - if (r < 0) - return r; - - w = json_variant_unref(w); - - r = ntas_build_json(link, &w); - if (r < 0) - return r; - - r = json_variant_merge(&v, w); - if (r < 0) - return r; - - w = json_variant_unref(w); - - r = dns_misc_build_json(link, &w); - if (r < 0) - return r; - - r = json_variant_merge(&v, w); - if (r < 0) - return r; - - w = json_variant_unref(w); - - r = addresses_build_json(link->addresses, &w); - if (r < 0) - return r; - - r = json_variant_merge(&v, w); - if (r < 0) - return r; - - w = json_variant_unref(w); - - r = neighbors_build_json(link->neighbors, &w); - if (r < 0) - return r; - - r = json_variant_merge(&v, w); - if (r < 0) - return r; - - w = json_variant_unref(w); - - r = nexthops_build_json(link->nexthops, &w); - if (r < 0) - return r; - - r = json_variant_merge(&v, w); - if (r < 0) - return r; - - w = json_variant_unref(w); - - r = routes_build_json(link->routes, &w); - if (r < 0) - return r; - - r = json_variant_merge(&v, w); - if (r < 0) - return r; - - w = json_variant_unref(w); - - r = dhcp_server_build_json(link, &w); - if (r < 0) - return r; - - r = json_variant_merge(&v, w); + r = dhcp_server_append_json(link, &v); if (r < 0) return r; @@ -1439,81 +1142,54 @@ int link_build_json(Link *link, JsonVariant **ret) { return 0; } -static int link_json_compare(JsonVariant * const *a, JsonVariant * const *b) { - int64_t index_a, index_b; - - assert(a && *a); - assert(b && *b); - - index_a = json_variant_integer(json_variant_by_key(*a, "Index")); - index_b = json_variant_integer(json_variant_by_key(*b, "Index")); - - return CMP(index_a, index_b); -} - -static int links_build_json(Manager *manager, JsonVariant **ret) { - JsonVariant **elements = NULL; - Link *link; - size_t n = 0; +static int links_append_json(Manager *manager, JsonVariant **v) { + _cleanup_(json_variant_unrefp) JsonVariant *array = NULL; + _cleanup_free_ Link **links = NULL; + size_t n_links = 0; int r; assert(manager); - assert(ret); + assert(v); - CLEANUP_ARRAY(elements, n, json_variant_unref_many); + r = hashmap_dump_sorted(manager->links_by_index, (void***) &links, &n_links); + if (r < 0) + return r; - elements = new(JsonVariant*, hashmap_size(manager->links_by_index)); - if (!elements) - return -ENOMEM; + FOREACH_ARRAY(link, links, n_links) { + _cleanup_(json_variant_unrefp) JsonVariant *e = NULL; - HASHMAP_FOREACH(link, manager->links_by_index) { - r = link_build_json(link, elements + n); + r = link_build_json(*link, &e); + if (r < 0) + return r; + + r = json_variant_append_array(&array, e); if (r < 0) return r; - n++; } - typesafe_qsort(elements, n, link_json_compare); - - return json_build(ret, JSON_BUILD_OBJECT(JSON_BUILD_PAIR("Interfaces", JSON_BUILD_VARIANT_ARRAY(elements, n)))); + return json_append_one(v, "Interfaces", array); } int manager_build_json(Manager *manager, JsonVariant **ret) { - _cleanup_(json_variant_unrefp) JsonVariant *v = NULL, *w = NULL; + _cleanup_(json_variant_unrefp) JsonVariant *v = NULL; int r; assert(manager); assert(ret); - r = links_build_json(manager, &v); + r = links_append_json(manager, &v); if (r < 0) return r; - r = nexthops_build_json(manager->nexthops, &w); + r = nexthops_append_json(manager->nexthops, &v); if (r < 0) return r; - r = json_variant_merge(&v, w); + r = routes_append_json(manager->routes, &v); if (r < 0) return r; - w = json_variant_unref(w); - - r = routes_build_json(manager->routes, &w); - if (r < 0) - return r; - - r = json_variant_merge(&v, w); - if (r < 0) - return r; - - w = json_variant_unref(w); - - r = routing_policy_rules_build_json(manager->rules, &w); - if (r < 0) - return r; - - r = json_variant_merge(&v, w); + r = routing_policy_rules_append_json(manager->rules, &v); if (r < 0) return r; diff --git a/src/shared/logs-show.c b/src/shared/logs-show.c index 7582dc9ace..abe11b817c 100644 --- a/src/shared/logs-show.c +++ b/src/shared/logs-show.c @@ -938,22 +938,21 @@ void json_escape( typedef struct JsonData { JsonVariant* name; - size_t n_values; - JsonVariant* values[]; + JsonVariant* values; } JsonData; -static JsonData* json_data_free(struct JsonData *d) { +static JsonData* json_data_free(JsonData *d) { if (!d) return NULL; json_variant_unref(d->name); - - FOREACH_ARRAY(v, d->values, d->n_values) - json_variant_unref(*v); + json_variant_unref(d->values); return mfree(d); } +DEFINE_TRIVIAL_CLEANUP_FUNC(JsonData*, json_data_free); + DEFINE_HASH_OPS_WITH_VALUE_DESTRUCTOR(json_data_hash_ops_free, char, string_hash_func, string_compare_func, JsonData, json_data_free); @@ -966,7 +965,7 @@ static int update_json_data( size_t size) { _cleanup_(json_variant_unrefp) JsonVariant *v = NULL; - JsonData *d = NULL; + JsonData *d; int r; assert(name); @@ -986,35 +985,31 @@ static int update_json_data( d = hashmap_get(h, name); if (d) { - JsonData *w; + r = json_variant_append_array(&d->values, v); + if (r < 0) + return log_error_errno(r, "Failed to append JSON value into array: %m"); + } else { + _cleanup_(json_data_freep) JsonData *e = NULL; - w = realloc(d, offsetof(JsonData, values) + sizeof(JsonVariant*) * (d->n_values + 1)); - if (!w) + e = new0(JsonData, 1); + if (!e) return log_oom(); - d = w; - assert_se(hashmap_update(h, json_variant_string(d->name), d) >= 0); - } else { - _cleanup_(json_variant_unrefp) JsonVariant *n = NULL; - - r = json_variant_new_string(&n, name); + r = json_variant_new_string(&e->name, name); if (r < 0) return log_error_errno(r, "Failed to allocate JSON name variant: %m"); - d = malloc0(offsetof(JsonData, values) + sizeof(JsonVariant*)); - if (!d) - return log_oom(); + r = json_variant_append_array(&e->values, v); + if (r < 0) + return log_error_errno(r, "Failed to create JSON value array: %m"); - r = hashmap_put(h, json_variant_string(n), d); - if (r < 0) { - free(d); - return log_error_errno(r, "Failed to insert JSON name into hashmap: %m"); - } + r = hashmap_put(h, json_variant_string(e->name), e); + if (r < 0) + return log_error_errno(r, "Failed to insert JSON data into hashmap: %m"); - d->name = TAKE_PTR(n); + TAKE_PTR(e); } - d->values[d->n_values++] = TAKE_PTR(v); return 0; } @@ -1156,28 +1151,20 @@ static int output_json( CLEANUP_ARRAY(array, n, json_variant_unref_many); HASHMAP_FOREACH(d, h) { - assert(d->n_values > 0); + assert(json_variant_elements(d->values) > 0); array[n++] = json_variant_ref(d->name); - if (d->n_values == 1) - array[n++] = json_variant_ref(d->values[0]); - else { - _cleanup_(json_variant_unrefp) JsonVariant *q = NULL; - - r = json_variant_new_array(&q, d->values, d->n_values); - if (r < 0) - return log_error_errno(r, "Failed to create JSON array: %m"); - - array[n++] = TAKE_PTR(q); - } + if (json_variant_elements(d->values) == 1) + array[n++] = json_variant_ref(json_variant_by_index(d->values, 0)); + else + array[n++] = json_variant_ref(d->values); } r = json_variant_new_object(&object, array, n); if (r < 0) return log_error_errno(r, "Failed to allocate JSON object: %m"); - return json_variant_dump(object, output_mode_to_json_format_flags(mode) | (FLAGS_SET(flags, OUTPUT_COLOR) ? JSON_FORMAT_COLOR : 0), diff --git a/src/shared/tpm2-util.c b/src/shared/tpm2-util.c index cf2a97dde0..0ebee68463 100644 --- a/src/shared/tpm2-util.c +++ b/src/shared/tpm2-util.c @@ -3838,34 +3838,30 @@ int tpm2_pcr_mask_from_string(const char *arg, uint32_t *ret_mask) { int tpm2_make_pcr_json_array(uint32_t pcr_mask, JsonVariant **ret) { _cleanup_(json_variant_unrefp) JsonVariant *a = NULL; - JsonVariant* pcr_array[TPM2_PCRS_MAX]; - unsigned n_pcrs = 0; int r; - for (size_t i = 0; i < ELEMENTSOF(pcr_array); i++) { + assert(ret); + + for (size_t i = 0; i < TPM2_PCRS_MAX; i++) { + _cleanup_(json_variant_unrefp) JsonVariant *e = NULL; + if ((pcr_mask & (UINT32_C(1) << i)) == 0) continue; - r = json_variant_new_integer(pcr_array + n_pcrs, i); + r = json_variant_new_integer(&e, i); if (r < 0) - goto finish; + return r; - n_pcrs++; + r = json_variant_append_array(&a, e); + if (r < 0) + return r; } - r = json_variant_new_array(&a, pcr_array, n_pcrs); - if (r < 0) - goto finish; + if (!a) + return json_variant_new_array(ret, NULL, 0); - if (ret) - *ret = TAKE_PTR(a); - r = 0; - -finish: - FOREACH_ARRAY(v, pcr_array, n_pcrs) - json_variant_unref(*v); - - return r; + *ret = TAKE_PTR(a); + return 0; } int tpm2_parse_pcr_json_array(JsonVariant *v, uint32_t *ret) { diff --git a/src/test/test-hashmap-plain.c b/src/test/test-hashmap-plain.c index 560a64b959..a821d412b7 100644 --- a/src/test/test-hashmap-plain.c +++ b/src/test/test-hashmap-plain.c @@ -963,6 +963,44 @@ TEST(string_strv_hashmap) { assert_se(strv_equal(s, STRV_MAKE("bar", "BAR"))); } +TEST(hashmap_dump_sorted) { + static void * const expected[] = { UINT_TO_PTR(123U), UINT_TO_PTR(12U), UINT_TO_PTR(345U), }; + _cleanup_hashmap_free_ Hashmap *m = NULL; + _cleanup_free_ void **vals = NULL; + size_t n; + + assert_se(m = hashmap_new(&string_hash_ops)); + + assert_se(hashmap_dump_sorted(m, &vals, &n) >= 0); + assert_se(n == 0); + assert_se(!vals); + + assert_se(hashmap_put(m, "key 0", expected[0]) == 1); + assert_se(hashmap_put(m, "key 1", expected[1]) == 1); + assert_se(hashmap_put(m, "key 2", expected[2]) == 1); + + assert_se(hashmap_dump_sorted(m, &vals, &n) >= 0); + assert_se(n == ELEMENTSOF(expected)); + assert_se(memcmp(vals, expected, n * sizeof(void*)) == 0); + + vals = mfree(vals); + m = hashmap_free(m); + + assert_se(m = hashmap_new(NULL)); + + assert_se(hashmap_dump_sorted(m, &vals, &n) >= 0); + assert_se(n == 0); + assert_se(!vals); + + assert_se(hashmap_put(m, UINT_TO_PTR(333U), expected[2]) == 1); + assert_se(hashmap_put(m, UINT_TO_PTR(222U), expected[1]) == 1); + assert_se(hashmap_put(m, UINT_TO_PTR(111U), expected[0]) == 1); + + assert_se(hashmap_dump_sorted(m, &vals, &n) >= 0); + assert_se(n == ELEMENTSOF(expected)); + assert_se(memcmp(vals, expected, n * sizeof(void*)) == 0); +} + /* Signal to test-hashmap.c that tests from this compilation unit were run. */ extern int n_extern_tests_run; TEST(ensure_extern_hashmap_tests) {