Merge pull request #28012 from poettering/resolvectl-dump-cache

add "resolvectl show-cache" call and IPC backend for that
This commit is contained in:
Lennart Poettering
2023-06-14 10:33:13 +02:00
committed by GitHub
11 changed files with 345 additions and 32 deletions

View File

@@ -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>

View File

@@ -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;
@@ -2807,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:");
@@ -2913,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"
@@ -3559,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 },
{}
};

View File

@@ -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;

View File

@@ -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);

View File

@@ -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;

View File

@@ -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) {
@@ -368,6 +371,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);

View File

@@ -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)));
}

View File

@@ -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);

View File

@@ -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");

View File

@@ -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;

View File

@@ -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);