diff options
author | Lennart Poettering <lennart@poettering.net> | 2023-06-12 16:46:04 +0200 |
---|---|---|
committer | Lennart Poettering <lennart@poettering.net> | 2023-06-12 22:21:26 +0200 |
commit | 6050e8b550c69439f17621a77f3e27ad051aad6a (patch) | |
tree | aa6d53ac3efaafb92780684c12dfecefa592b425 | |
parent | resolved: add DNS_RESOURCE_KEY_TO_STRING() macro helper (diff) | |
download | systemd-6050e8b550c69439f17621a77f3e27ad051aad6a.tar.xz systemd-6050e8b550c69439f17621a77f3e27ad051aad6a.zip |
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.
-rw-r--r-- | man/resolvectl.xml | 7 | ||||
-rw-r--r-- | src/resolve/resolvectl.c | 153 |
2 files changed, 160 insertions, 0 deletions
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.</para></listitem> </varlistentry> + <varlistentry> + <term><command>show-cache</command></term> + + <listitem><para>Show current cache content, per scope. Use <option>--json=</option> to enable JSON + output.</para></listitem> + </varlistentry> + <xi:include href="systemctl.xml" xpointer="log-level" /> </variablelist> </refsect1> 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 }, {} }; |