From 6b9f63cb3fdbc8e365aa55bef219ced91c10c103 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Mon, 12 Jun 2023 16:37:37 +0200 Subject: [PATCH 1/5] json: add json_dispatch_variant_noref() helper This is identical to json_dispatch_variant() but doesn't increase the ref counter of the variant. This is useful for taking JSON objects apart locally without the intention to keep any component pinned for longer than the local stack frame. --- src/shared/json.c | 10 ++++++++++ src/shared/json.h | 1 + 2 files changed, 11 insertions(+) diff --git a/src/shared/json.c b/src/shared/json.c index 26d99a5fba..417d17df34 100644 --- a/src/shared/json.c +++ b/src/shared/json.c @@ -4642,10 +4642,20 @@ int json_dispatch_variant(const char *name, JsonVariant *variant, JsonDispatchFl JsonVariant **p = ASSERT_PTR(userdata); assert(variant); + /* Takes a reference */ JSON_VARIANT_REPLACE(*p, json_variant_ref(variant)); return 0; } +int json_dispatch_variant_noref(const char *name, JsonVariant *variant, JsonDispatchFlags flags, void *userdata) { + JsonVariant **p = ASSERT_PTR(userdata); + assert(variant); + + /* Doesn't take a reference */ + *p = variant; + return 0; +} + int json_dispatch_uid_gid(const char *name, JsonVariant *variant, JsonDispatchFlags flags, void *userdata) { uid_t *uid = userdata; uint64_t k; diff --git a/src/shared/json.h b/src/shared/json.h index 5d79472351..1356d0827b 100644 --- a/src/shared/json.h +++ b/src/shared/json.h @@ -387,6 +387,7 @@ int json_dispatch_strv(const char *name, JsonVariant *variant, JsonDispatchFlags int json_dispatch_boolean(const char *name, JsonVariant *variant, JsonDispatchFlags flags, void *userdata); int json_dispatch_tristate(const char *name, JsonVariant *variant, JsonDispatchFlags flags, void *userdata); int json_dispatch_variant(const char *name, JsonVariant *variant, JsonDispatchFlags flags, void *userdata); +int json_dispatch_variant_noref(const char *name, JsonVariant *variant, JsonDispatchFlags flags, void *userdata); int json_dispatch_int64(const char *name, JsonVariant *variant, JsonDispatchFlags flags, void *userdata); int json_dispatch_uint64(const char *name, JsonVariant *variant, JsonDispatchFlags flags, void *userdata); int json_dispatch_uint32(const char *name, JsonVariant *variant, JsonDispatchFlags flags, void *userdata); From e0930aa6ffabb0931d7e9e387b180fca8756ad8e Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Mon, 12 Jun 2023 16:40:59 +0200 Subject: [PATCH 2/5] resolved: add DumpCache varlink call for acquiring a complete dump of all of resolved's RR caches This adds a simple varlink call io.systemd.Resolve.Monitor.DumpCache to the existing io.systemd.Resolve.Monitor service. It compiles a JSON object containing the per-scope cache entries and returns it. Replaces: #20053 #19104 Fixes: #14796 --- src/resolve/resolved-dns-cache.c | 80 ++++++++++++++++++++++++++++++++ src/resolve/resolved-dns-cache.h | 2 + src/resolve/resolved-dns-scope.c | 20 ++++++++ src/resolve/resolved-dns-scope.h | 2 + src/resolve/resolved-varlink.c | 40 ++++++++++++++-- 5 files changed, 141 insertions(+), 3 deletions(-) diff --git a/src/resolve/resolved-dns-cache.c b/src/resolve/resolved-dns-cache.c index 2f97cf7730..089ed3aa0a 100644 --- a/src/resolve/resolved-dns-cache.c +++ b/src/resolve/resolved-dns-cache.c @@ -1351,6 +1351,86 @@ void dns_cache_dump(DnsCache *cache, FILE *f) { } } +int dns_cache_dump_to_json(DnsCache *cache, JsonVariant **ret) { + _cleanup_(json_variant_unrefp) JsonVariant *c = NULL; + DnsCacheItem *i; + int r; + + assert(cache); + assert(ret); + + HASHMAP_FOREACH(i, cache->by_key) { + _cleanup_(json_variant_unrefp) JsonVariant *d = NULL, *k = NULL; + + r = dns_resource_key_to_json(i->key, &k); + if (r < 0) + return r; + + if (i->rr) { + _cleanup_(json_variant_unrefp) JsonVariant *l = NULL; + + LIST_FOREACH(by_key, j, i) { + _cleanup_(json_variant_unrefp) JsonVariant *rj = NULL, *item = NULL; + + assert(j->rr); + + r = dns_resource_record_to_json(j->rr, &rj); + if (r < 0) + return r; + + r = dns_resource_record_to_wire_format(j->rr, /* canonical= */ false); /* don't use DNSSEC canonical format, since it removes casing, but we want that for DNS_SD compat */ + if (r < 0) + return r; + + r = json_build(&item, JSON_BUILD_OBJECT( + JSON_BUILD_PAIR_VARIANT("rr", rj), + JSON_BUILD_PAIR_BASE64("raw", j->rr->wire_format, j->rr->wire_format_size))); + if (r < 0) + return r; + + r = json_variant_append_array(&l, item); + if (r < 0) + return r; + } + + if (!l) { + r = json_variant_new_array(&l, NULL, 0); + if (r < 0) + return r; + } + + r = json_build(&d, + JSON_BUILD_OBJECT( + JSON_BUILD_PAIR_VARIANT("key", k), + JSON_BUILD_PAIR_VARIANT("rrs", l), + JSON_BUILD_PAIR_UNSIGNED("until", i->until))); + } else if (i->type == DNS_CACHE_NODATA) { + r = json_build(&d, + JSON_BUILD_OBJECT( + JSON_BUILD_PAIR_VARIANT("key", k), + JSON_BUILD_PAIR_EMPTY_ARRAY("rrs"), + JSON_BUILD_PAIR_UNSIGNED("until", i->until))); + } else + r = json_build(&d, + JSON_BUILD_OBJECT( + JSON_BUILD_PAIR_VARIANT("key", k), + JSON_BUILD_PAIR_STRING("type", dns_cache_item_type_to_string(i)), + JSON_BUILD_PAIR_UNSIGNED("until", i->until))); + if (r < 0) + return r; + + r = json_variant_append_array(&c, d); + if (r < 0) + return r; + } + + if (!c) + return json_variant_new_array(ret, NULL, 0); + + *ret = TAKE_PTR(c); + return 0; +} + bool dns_cache_is_empty(DnsCache *cache) { if (!cache) return true; diff --git a/src/resolve/resolved-dns-cache.h b/src/resolve/resolved-dns-cache.h index 42e744a910..bc045bc80c 100644 --- a/src/resolve/resolved-dns-cache.h +++ b/src/resolve/resolved-dns-cache.h @@ -50,6 +50,8 @@ int dns_cache_lookup( int dns_cache_check_conflicts(DnsCache *cache, DnsResourceRecord *rr, int owner_family, const union in_addr_union *owner_address); void dns_cache_dump(DnsCache *cache, FILE *f); +int dns_cache_dump_to_json(DnsCache *cache, JsonVariant **ret); + bool dns_cache_is_empty(DnsCache *cache); unsigned dns_cache_size(DnsCache *cache); diff --git a/src/resolve/resolved-dns-scope.c b/src/resolve/resolved-dns-scope.c index 45f1d36311..35085a6ef5 100644 --- a/src/resolve/resolved-dns-scope.c +++ b/src/resolve/resolved-dns-scope.c @@ -1643,3 +1643,23 @@ bool dns_scope_is_default_route(DnsScope *scope) { * volunteer as default route. */ return !dns_scope_has_route_only_domains(scope); } + +int dns_scope_dump_cache_to_json(DnsScope *scope, JsonVariant **ret) { + _cleanup_(json_variant_unrefp) JsonVariant *cache = NULL; + int r; + + assert(scope); + assert(ret); + + r = dns_cache_dump_to_json(&scope->cache, &cache); + if (r < 0) + return r; + + return json_build(ret, + JSON_BUILD_OBJECT( + JSON_BUILD_PAIR_STRING("protocol", dns_protocol_to_string(scope->protocol)), + JSON_BUILD_PAIR_CONDITION(scope->family != AF_UNSPEC, "family", JSON_BUILD_INTEGER(scope->family)), + JSON_BUILD_PAIR_CONDITION(scope->link, "ifindex", JSON_BUILD_INTEGER(scope->link ? scope->link->ifindex : 0)), + JSON_BUILD_PAIR_CONDITION(scope->link, "ifname", JSON_BUILD_STRING(scope->link ? scope->link->ifname : NULL)), + JSON_BUILD_PAIR_VARIANT("cache", cache))); +} diff --git a/src/resolve/resolved-dns-scope.h b/src/resolve/resolved-dns-scope.h index 1f9d22b7d1..ca33fd007a 100644 --- a/src/resolve/resolved-dns-scope.h +++ b/src/resolve/resolved-dns-scope.h @@ -110,3 +110,5 @@ int dns_scope_add_dnssd_services(DnsScope *scope); int dns_scope_remove_dnssd_services(DnsScope *scope); bool dns_scope_is_default_route(DnsScope *scope); + +int dns_scope_dump_cache_to_json(DnsScope *scope, JsonVariant **ret); diff --git a/src/resolve/resolved-varlink.c b/src/resolve/resolved-varlink.c index f878d9ee3f..fc224f627e 100644 --- a/src/resolve/resolved-varlink.c +++ b/src/resolve/resolved-varlink.c @@ -563,6 +563,40 @@ static int vl_method_subscribe_dns_resolves(Varlink *link, JsonVariant *paramete return 1; } +static int vl_method_dump_cache(Varlink *link, JsonVariant *parameters, VarlinkMethodFlags flags, void *userdata) { + _cleanup_(json_variant_unrefp) JsonVariant *list = NULL; + Manager *m; + int r; + + assert(link); + + if (json_variant_elements(parameters) > 0) + return varlink_error_invalid_parameter(link, parameters); + + m = ASSERT_PTR(varlink_server_get_userdata(varlink_get_server(link))); + + LIST_FOREACH(scopes, s, m->dns_scopes) { + _cleanup_(json_variant_unrefp) JsonVariant *j = NULL; + + r = dns_scope_dump_cache_to_json(s, &j); + if (r < 0) + return r; + + r = json_variant_append_array(&list, j); + if (r < 0) + return r; + } + + if (!list) { + r = json_variant_new_array(&list, NULL, 0); + if (r < 0) + return r; + } + + return varlink_replyb(link, JSON_BUILD_OBJECT( + JSON_BUILD_PAIR("dump", JSON_BUILD_VARIANT(list)))); +} + static int varlink_monitor_server_init(Manager *m) { _cleanup_(varlink_server_unrefp) VarlinkServer *server = NULL; int r; @@ -578,10 +612,10 @@ static int varlink_monitor_server_init(Manager *m) { varlink_server_set_userdata(server, m); - r = varlink_server_bind_method( + r = varlink_server_bind_method_many( server, - "io.systemd.Resolve.Monitor.SubscribeQueryResults", - vl_method_subscribe_dns_resolves); + "io.systemd.Resolve.Monitor.SubscribeQueryResults", vl_method_subscribe_dns_resolves, + "io.systemd.Resolve.Monitor.DumpCache", vl_method_dump_cache); if (r < 0) return log_error_errno(r, "Failed to register varlink methods: %m"); From ce74fb09050831b6e76e134620f5caadff3b25ef Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Mon, 12 Jun 2023 16:44:40 +0200 Subject: [PATCH 3/5] resolved: add dns_resource_key_from_json() helper It reverse what dns_resource_key_to_json(), i.e. turns JSON data into a parsed DnsResourceKey object. Ultimately this just moves a client-side local wrapper into generic code. Nothing truly new here. --- src/resolve/resolvectl.c | 30 +----------------------------- src/resolve/resolved-dns-rr.c | 28 ++++++++++++++++++++++++++++ src/resolve/resolved-dns-rr.h | 1 + 3 files changed, 30 insertions(+), 29 deletions(-) diff --git a/src/resolve/resolvectl.c b/src/resolve/resolvectl.c index e65fbbcbab..3de0a4e9d9 100644 --- a/src/resolve/resolvectl.c +++ b/src/resolve/resolvectl.c @@ -2586,34 +2586,6 @@ static int verb_log_level(int argc, char *argv[], void *userdata) { return verb_log_control_common(bus, "org.freedesktop.resolve1", argv[0], argc == 2 ? argv[1] : NULL); } -static int monitor_rkey_from_json(JsonVariant *v, DnsResourceKey **ret_key) { - _cleanup_(dns_resource_key_unrefp) DnsResourceKey *key = NULL; - uint16_t type = 0, class = 0; - const char *name = NULL; - int r; - - JsonDispatch dispatch_table[] = { - { "class", JSON_VARIANT_INTEGER, json_dispatch_uint16, PTR_TO_SIZE(&class), JSON_MANDATORY }, - { "type", JSON_VARIANT_INTEGER, json_dispatch_uint16, PTR_TO_SIZE(&type), JSON_MANDATORY }, - { "name", JSON_VARIANT_STRING, json_dispatch_const_string, PTR_TO_SIZE(&name), JSON_MANDATORY }, - {} - }; - - assert(v); - assert(ret_key); - - r = json_dispatch(v, dispatch_table, NULL, 0, NULL); - if (r < 0) - return r; - - key = dns_resource_key_new(class, type, name); - if (!key) - return -ENOMEM; - - *ret_key = TAKE_PTR(key); - return 0; -} - static int print_question(char prefix, const char *color, JsonVariant *question) { JsonVariant *q = NULL; int r; @@ -2624,7 +2596,7 @@ static int print_question(char prefix, const char *color, JsonVariant *question) _cleanup_(dns_resource_key_unrefp) DnsResourceKey *key = NULL; char buf[DNS_RESOURCE_KEY_STRING_MAX]; - r = monitor_rkey_from_json(q, &key); + r = dns_resource_key_from_json(q, &key); if (r < 0) { log_warning_errno(r, "Received monitor message with invalid question key, ignoring: %m"); continue; diff --git a/src/resolve/resolved-dns-rr.c b/src/resolve/resolved-dns-rr.c index 603bb1a10d..f6344542d6 100644 --- a/src/resolve/resolved-dns-rr.c +++ b/src/resolve/resolved-dns-rr.c @@ -1853,6 +1853,34 @@ int dns_resource_key_to_json(DnsResourceKey *key, JsonVariant **ret) { JSON_BUILD_PAIR("name", JSON_BUILD_STRING(dns_resource_key_name(key))))); } +int dns_resource_key_from_json(JsonVariant *v, DnsResourceKey **ret) { + _cleanup_(dns_resource_key_unrefp) DnsResourceKey *key = NULL; + uint16_t type = 0, class = 0; + const char *name = NULL; + int r; + + JsonDispatch dispatch_table[] = { + { "class", JSON_VARIANT_INTEGER, json_dispatch_uint16, PTR_TO_SIZE(&class), JSON_MANDATORY }, + { "type", JSON_VARIANT_INTEGER, json_dispatch_uint16, PTR_TO_SIZE(&type), JSON_MANDATORY }, + { "name", JSON_VARIANT_STRING, json_dispatch_const_string, PTR_TO_SIZE(&name), JSON_MANDATORY }, + {} + }; + + assert(v); + assert(ret); + + r = json_dispatch(v, dispatch_table, NULL, 0, NULL); + if (r < 0) + return r; + + key = dns_resource_key_new(class, type, name); + if (!key) + return -ENOMEM; + + *ret = TAKE_PTR(key); + return 0; +} + static int type_bitmap_to_json(Bitmap *b, JsonVariant **ret) { _cleanup_(json_variant_unrefp) JsonVariant *l = NULL; unsigned t; diff --git a/src/resolve/resolved-dns-rr.h b/src/resolve/resolved-dns-rr.h index 024cfb8744..a3fa24eaf9 100644 --- a/src/resolve/resolved-dns-rr.h +++ b/src/resolve/resolved-dns-rr.h @@ -368,6 +368,7 @@ int dns_txt_item_new_empty(DnsTxtItem **ret); int dns_resource_record_new_from_raw(DnsResourceRecord **ret, const void *data, size_t size); int dns_resource_key_to_json(DnsResourceKey *key, JsonVariant **ret); +int dns_resource_key_from_json(JsonVariant *v, DnsResourceKey **ret); int dns_resource_record_to_json(DnsResourceRecord *rr, JsonVariant **ret); void dns_resource_record_hash_func(const DnsResourceRecord *i, struct siphash *state); From 25165c1deaaa69ba10a8231200739b9d932f78a7 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Mon, 12 Jun 2023 16:45:25 +0200 Subject: [PATCH 4/5] resolved: add DNS_RESOURCE_KEY_TO_STRING() macro helper This does the usual compound init trick to get formatted strings of stuff. We should probably port various pieces of code over to using this. --- src/resolve/resolved-dns-rr.h | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/resolve/resolved-dns-rr.h b/src/resolve/resolved-dns-rr.h index a3fa24eaf9..fd15cc343d 100644 --- a/src/resolve/resolved-dns-rr.h +++ b/src/resolve/resolved-dns-rr.h @@ -317,6 +317,9 @@ int dns_resource_key_match_soa(const DnsResourceKey *key, const DnsResourceKey * char* dns_resource_key_to_string(const DnsResourceKey *key, char *buf, size_t buf_size); ssize_t dns_resource_record_payload(DnsResourceRecord *rr, void **out); +#define DNS_RESOURCE_KEY_TO_STRING(key) \ + dns_resource_key_to_string(key, (char[DNS_RESOURCE_KEY_STRING_MAX]) {}, DNS_RESOURCE_KEY_STRING_MAX) + DEFINE_TRIVIAL_CLEANUP_FUNC(DnsResourceKey*, dns_resource_key_unref); static inline bool dns_key_is_shared(const DnsResourceKey *key) { From 6050e8b550c69439f17621a77f3e27ad051aad6a Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Mon, 12 Jun 2023 16:46:04 +0200 Subject: [PATCH 5/5] resolvectl: add resolvectl command for dumping cache contents A wrapper around the new varlink call, showing the data either in its native JSON or in a more human readable textual form. --- man/resolvectl.xml | 7 ++ src/resolve/resolvectl.c | 153 +++++++++++++++++++++++++++++++++++++++ 2 files changed, 160 insertions(+) diff --git a/man/resolvectl.xml b/man/resolvectl.xml index c966ca67bd..37a51b4760 100644 --- a/man/resolvectl.xml +++ b/man/resolvectl.xml @@ -212,6 +212,13 @@ output. + + show-cache + + Show current cache content, per scope. Use to enable JSON + output. + + diff --git a/src/resolve/resolvectl.c b/src/resolve/resolvectl.c index 3de0a4e9d9..c7ec3de588 100644 --- a/src/resolve/resolvectl.c +++ b/src/resolve/resolvectl.c @@ -2779,6 +2779,157 @@ static int verb_monitor(int argc, char *argv[], void *userdata) { return c; } +static int dump_cache_item(JsonVariant *item) { + + struct item_info { + JsonVariant *key; + JsonVariant *rrs; + const char *type; + uint64_t until; + } item_info = {}; + + static const JsonDispatch dispatch_table[] = { + { "key", JSON_VARIANT_OBJECT, json_dispatch_variant_noref, offsetof(struct item_info, key), JSON_MANDATORY }, + { "rrs", JSON_VARIANT_ARRAY, json_dispatch_variant_noref, offsetof(struct item_info, rrs), 0 }, + { "type", JSON_VARIANT_STRING, json_dispatch_const_string, offsetof(struct item_info, type), 0 }, + { "until", JSON_VARIANT_UNSIGNED, json_dispatch_uint64, offsetof(struct item_info, until), 0 }, + {}, + }; + + _cleanup_(dns_resource_key_unrefp) DnsResourceKey *k = NULL; + int r, c = 0; + + r = json_dispatch(item, dispatch_table, NULL, JSON_LOG, &item_info); + if (r < 0) + return r; + + r = dns_resource_key_from_json(item_info.key, &k); + if (r < 0) + return log_error_errno(r, "Failed to turn JSON data to resource key: %m"); + + if (item_info.type) + printf("%s %s%s%s\n", DNS_RESOURCE_KEY_TO_STRING(k), ansi_highlight_red(), item_info.type, ansi_normal()); + else { + JsonVariant *i; + + JSON_VARIANT_ARRAY_FOREACH(i, item_info.rrs) { + _cleanup_(dns_resource_record_unrefp) DnsResourceRecord *rr = NULL; + _cleanup_free_ void *data = NULL; + JsonVariant *raw; + size_t size; + + raw = json_variant_by_key(i, "raw"); + if (!raw) + return log_error_errno(SYNTHETIC_ERRNO(ENOTRECOVERABLE), "raw field missing from RR JSON data."); + + r = json_variant_unbase64(raw, &data, &size); + if (r < 0) + return log_error_errno(r, "Unable to decode raw RR JSON data: %m"); + + r = dns_resource_record_new_from_raw(&rr, data, size); + if (r < 0) + return log_error_errno(r, "Failed to parse DNS data: %m"); + + printf("%s\n", dns_resource_record_to_string(rr)); + c++; + } + } + + return c; +} + +static int dump_cache_scope(JsonVariant *scope) { + + struct scope_info { + const char *protocol; + int family; + int ifindex; + const char *ifname; + JsonVariant *cache; + } scope_info = { + .family = AF_UNSPEC, + }; + JsonVariant *i; + int r, c = 0; + + static const JsonDispatch dispatch_table[] = { + { "protocol", JSON_VARIANT_STRING, json_dispatch_const_string, offsetof(struct scope_info, protocol), JSON_MANDATORY }, + { "family", JSON_VARIANT_INTEGER, json_dispatch_int, offsetof(struct scope_info, family), 0 }, + { "ifindex", JSON_VARIANT_INTEGER, json_dispatch_int, offsetof(struct scope_info, ifindex), 0 }, + { "ifname", JSON_VARIANT_STRING, json_dispatch_const_string, offsetof(struct scope_info, ifname), 0 }, + { "cache", JSON_VARIANT_ARRAY, json_dispatch_variant_noref, offsetof(struct scope_info, cache), JSON_MANDATORY }, + {}, + }; + + r = json_dispatch(scope, dispatch_table, NULL, JSON_LOG, &scope_info); + if (r < 0) + return r; + + printf("%sScope protocol=%s", ansi_underline(), scope_info.protocol); + + if (scope_info.family != AF_UNSPEC) + printf(" family=%s", af_to_name(scope_info.family)); + + if (scope_info.ifindex > 0) + printf(" ifindex=%i", scope_info.ifindex); + if (scope_info.ifname) + printf(" ifname=%s", scope_info.ifname); + + printf("%s\n", ansi_normal()); + + JSON_VARIANT_ARRAY_FOREACH(i, scope_info.cache) { + r = dump_cache_item(i); + if (r < 0) + return r; + + c += r; + } + + if (c == 0) + printf("%sNo entries.%s\n\n", ansi_grey(), ansi_normal()); + else + printf("\n"); + + return 0; +} + +static int verb_show_cache(int argc, char *argv[], void *userdata) { + _cleanup_(json_variant_unrefp) JsonVariant *d = NULL, *reply = NULL; + _cleanup_(varlink_unrefp) Varlink *vl = NULL; + int r; + + r = varlink_connect_address(&vl, "/run/systemd/resolve/io.systemd.Resolve.Monitor"); + if (r < 0) + return log_error_errno(r, "Failed to connect to query monitoring service /run/systemd/resolve/io.systemd.Resolve.Monitor: %m"); + + r = varlink_call(vl, "io.systemd.Resolve.Monitor.DumpCache", NULL, &reply, NULL, 0); + if (r < 0) + return log_error_errno(r, "Failed to issue DumpCache() varlink call: %m"); + + d = json_variant_by_key(reply, "dump"); + if (!d) + return log_error_errno(SYNTHETIC_ERRNO(ENOTRECOVERABLE), + "DumpCache() response is missing 'dump' key."); + + if (!json_variant_is_array(d)) + return log_error_errno(SYNTHETIC_ERRNO(ENOTRECOVERABLE), + "DumpCache() response 'dump' field not an array"); + + if (FLAGS_SET(arg_json_format_flags, JSON_FORMAT_OFF)) { + JsonVariant *i; + + JSON_VARIANT_ARRAY_FOREACH(i, d) { + r = dump_cache_scope(i); + if (r < 0) + return r; + } + + return 0; + } + + return json_variant_dump(d, arg_json_format_flags, NULL, NULL); +} + static void help_protocol_types(void) { if (arg_legend) puts("Known protocol types:"); @@ -2885,6 +3036,7 @@ static int native_help(void) { " flush-caches Flush all local DNS caches\n" " reset-server-features Forget learnt DNS server feature levels\n" " monitor Monitor DNS queries\n" + " show-cache Show cache contents\n" " dns [LINK [SERVER...]] Get/set per-interface DNS server address\n" " domain [LINK [DOMAIN...]] Get/set per-interface search domain\n" " default-route [LINK [BOOL]] Get/set per-interface default route flag\n" @@ -3531,6 +3683,7 @@ static int native_main(int argc, char *argv[], sd_bus *bus) { { "revert", VERB_ANY, 2, 0, verb_revert_link }, { "log-level", VERB_ANY, 2, 0, verb_log_level }, { "monitor", VERB_ANY, 1, 0, verb_monitor }, + { "show-cache", VERB_ANY, 1, 0, verb_show_cache }, {} };