]> git.ipfire.org Git - thirdparty/systemd.git/commitdiff
resolvectl: use varlink instead of dbus for ResolveService 41840/head
authorNick Rosbrook <enr0n@ubuntu.com>
Fri, 19 Jun 2026 19:01:03 +0000 (15:01 -0400)
committerNick Rosbrook <enr0n@ubuntu.com>
Fri, 26 Jun 2026 20:03:18 +0000 (16:03 -0400)
src/resolve/resolvectl.c
src/shared/resolve-varlink-util.c
src/shared/resolve-varlink-util.h

index 327cf10140719cc1911cfe0652605ee7ff4bd86b..af7ed4367266dbcb81372eb40fdf6aa3d8bb352e 100644 (file)
@@ -18,7 +18,6 @@
 #include "bus-common-errors.h"
 #include "bus-error.h"
 #include "bus-locator.h"
-#include "bus-message-util.h"
 #include "bus-util.h"
 #include "crypto-util.h"
 #include "dns-configuration.h"
@@ -27,7 +26,6 @@
 #include "dns-rr.h"
 #include "errno-list.h"
 #include "errno-util.h"
-#include "escape.h"
 #include "format-ifname.h"
 #include "format-table.h"
 #include "glyph-util.h"
@@ -851,18 +849,9 @@ static int verb_query(int argc, char *argv[], uintptr_t _data, void *userdata) {
         return ret;
 }
 
-static int resolve_service(sd_bus *bus, const char *name, const char *type, const char *domain) {
-        const char *canonical_name, *canonical_type, *canonical_domain;
-        _cleanup_(sd_bus_message_unrefp) sd_bus_message *req = NULL, *reply = NULL;
-        _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
-        size_t indent, sz;
-        uint64_t flags;
-        const char *p;
-        unsigned c;
-        usec_t ts;
+static int resolve_service(const char *name, const char *type, const char *domain) {
         int r;
 
-        assert(bus);
         assert(domain);
 
         name = empty_to_null(name);
@@ -875,141 +864,83 @@ static int resolve_service(sd_bus *bus, const char *name, const char *type, cons
         else
                 log_debug("Resolving service type %s (family %s, interface %s).", domain, af_to_name(arg_family) ?: "*", isempty(arg_ifname) ? "*" : arg_ifname);
 
-        r = bus_message_new_method_call(bus, &req, bus_resolve_mgr, "ResolveService");
-        if (r < 0)
-                return bus_log_create_error(r);
-
-        r = sd_bus_message_append(req, "isssit", arg_ifindex, name, type, domain, arg_family, arg_flags);
+        _cleanup_(sd_varlink_unrefp) sd_varlink *vl = NULL;
+        r = varlink_connect_with_query_timeout(&vl);
         if (r < 0)
-                return bus_log_create_error(r);
+                return r;
 
-        ts = now(CLOCK_MONOTONIC);
+        usec_t ts = now(CLOCK_MONOTONIC);
 
-        r = sd_bus_call(bus, req, SD_RESOLVED_QUERY_TIMEOUT_USEC, &error, &reply);
+        const char *error_id = NULL;
+        sd_json_variant *v = NULL;
+        r = sd_varlink_callbo(
+                        vl,
+                        "io.systemd.Resolve.ResolveService",
+                        &v,
+                        &error_id,
+                        SD_JSON_BUILD_PAIR_STRING("domain", domain),
+                        JSON_BUILD_PAIR_STRING_NON_EMPTY("name", name),
+                        JSON_BUILD_PAIR_STRING_NON_EMPTY("type", type),
+                        JSON_BUILD_PAIR_CONDITION_UNSIGNED(arg_ifindex > 0, "ifindex", arg_ifindex),
+                        JSON_BUILD_PAIR_CONDITION_UNSIGNED(arg_family != AF_UNSPEC, "family", arg_family),
+                        JSON_BUILD_PAIR_UNSIGNED_NON_ZERO("flags", arg_flags));
         if (r < 0)
-                return log_error_errno(r, "Resolve call failed: %s", bus_error_message(&error, r));
+                return log_error_errno(r, "Failed to issue varlink call: %m");
 
         ts = now(CLOCK_MONOTONIC) - ts;
 
-        r = sd_bus_message_enter_container(reply, 'a', "(qqqsa(iiay)s)");
-        if (r < 0)
-                return bus_log_parse_error(r);
-
-        indent =
-                (name ? strlen(name) + 1 : 0) +
-                (type ? strlen(type) + 1 : 0) +
-                strlen(domain) + 2;
+        if (!isempty(error_id))
+                return varlink_log_resolve_error(domain, error_id, v, /* warn_missing = */ true);
 
-        c = 0;
-        while ((r = sd_bus_message_enter_container(reply, 'r', "qqqsa(iiay)s")) > 0) {
-                uint16_t priority, weight, port;
-                const char *hostname, *canonical;
+        _cleanup_(resolve_service_reply_done) ResolveServiceReply reply = {};
+        r = dispatch_resolve_service_reply(/* name = */ NULL, v, SD_JSON_LOG, &reply);
+        if (r < 0)
+                return r;
 
-                r = sd_bus_message_read(reply, "qqqs", &priority, &weight, &port, &hostname);
-                if (r < 0)
-                        return bus_log_parse_error(r);
+        size_t indent = (name ? strlen(name) + 1 : 0) +
+                        (type ? strlen(type) + 1 : 0) +
+                        strlen(domain) + 2;
 
+        bool first = true;
+        FOREACH_ARRAY(service, reply.services, reply.n_services) {
                 if (name)
-                        printf("%*s%s", (int) strlen(name), c == 0 ? name : "", c == 0 ? "/" : " ");
+                        printf("%*s%s", (int) strlen(name), first ? name : "", first ? "/" : " ");
                 if (type)
-                        printf("%*s%s", (int) strlen(type), c == 0 ? type : "", c == 0 ? "/" : " ");
+                        printf("%*s%s", (int) strlen(type), first ? type : "", first ? "/" : " ");
 
                 printf("%*s%s %s:%u [priority=%u, weight=%u]\n",
-                       (int) strlen(domain), c == 0 ? domain : "",
-                       c == 0 ? ":" : " ",
-                       hostname, port,
-                       priority, weight);
-
-                r = sd_bus_message_enter_container(reply, 'a', "(iiay)");
-                if (r < 0)
-                        return bus_log_parse_error(r);
-
-                while ((r = sd_bus_message_enter_container(reply, 'r', "iiay")) > 0) {
+                       (int) strlen(domain),
+                       first ? domain : "",
+                       first ? ":" : " ",
+                       service->hostname,
+                       service->port,
+                       service->priority,
+                       service->weight);
+
+                FOREACH_ARRAY(address, service->addresses, service->n_addresses) {
                         _cleanup_free_ char *pretty = NULL;
-                        int ifindex, family, k;
-                        union in_addr_union a;
-
-                        assert_cc(sizeof(int) == sizeof(int32_t));
-
-                        r = sd_bus_message_read(reply, "i", &ifindex);
-                        if (r < 0)
-                                return bus_log_parse_error(r);
-
-                        sd_bus_error_free(&error);
-                        r = bus_message_read_in_addr_auto(reply, &error, &family, &a);
-                        if (r < 0 && !sd_bus_error_has_name(&error, SD_BUS_ERROR_INVALID_ARGS))
-                                return log_error_errno(r, "%s: systemd-resolved returned invalid result: %s", name, bus_error_message(&error, r));
-
-                        r = sd_bus_message_exit_container(reply);
-                        if (r < 0)
-                                return bus_log_parse_error(r);
-
-                        if (sd_bus_error_has_name(&error, SD_BUS_ERROR_INVALID_ARGS)) {
-                                log_debug_errno(r, "%s: systemd-resolved returned invalid result, ignoring: %s", name, bus_error_message(&error, r));
-                                continue;
-                        }
-
-                        r = in_addr_ifindex_to_string(family, &a, ifindex, &pretty);
+                        r = in_addr_ifindex_to_string(address->family, &address->in_addr.address, address->ifindex, &pretty);
                         if (r < 0)
                                 return log_error_errno(r, "Failed to print address for %s: %m", name);
 
-                        k = printf("%*s%s", (int) indent, "", pretty);
-                        print_ifindex_comment(k, ifindex);
+                        int k = printf("%*s%s", (int) indent, "", pretty);
+
+                        print_ifindex_comment(k, address->ifindex);
                         fputc('\n', stdout);
                 }
-                if (r < 0)
-                        return bus_log_parse_error(r);
-
-                r = sd_bus_message_exit_container(reply);
-                if (r < 0)
-                        return bus_log_parse_error(r);
-
-                r = sd_bus_message_read(reply, "s", &canonical);
-                if (r < 0)
-                        return bus_log_parse_error(r);
-
-                if (!streq(hostname, canonical))
-                        printf("%*s(%s)\n", (int) indent, "", canonical);
 
-                r = sd_bus_message_exit_container(reply);
-                if (r < 0)
-                        return bus_log_parse_error(r);
+                if (service->canonical_name && !streq_ptr(service->hostname, service->canonical_name))
+                        printf("%*s(%s)\n", (int) indent, "", service->canonical_name);
 
-                c++;
-        }
-        if (r < 0)
-                return bus_log_parse_error(r);
-
-        r = sd_bus_message_exit_container(reply);
-        if (r < 0)
-                return bus_log_parse_error(r);
-
-        r = sd_bus_message_enter_container(reply, 'a', "ay");
-        if (r < 0)
-                return bus_log_parse_error(r);
-
-        while ((r = sd_bus_message_read_array(reply, 'y', (const void**) &p, &sz)) > 0) {
-                _cleanup_free_ char *escaped = NULL;
-
-                escaped = cescape_length(p, sz);
-                if (!escaped)
-                        return log_oom();
-
-                printf("%*s%s\n", (int) indent, "", escaped);
+                first = false;
         }
-        if (r < 0)
-                return bus_log_parse_error(r);
-
-        r = sd_bus_message_exit_container(reply);
-        if (r < 0)
-                return bus_log_parse_error(r);
 
-        r = sd_bus_message_read(reply, "ssst", &canonical_name, &canonical_type, &canonical_domain, &flags);
-        if (r < 0)
-                return bus_log_parse_error(r);
+        STRV_FOREACH(p, reply.txt)
+                printf("%*s%s\n", (int) indent, "", *p);
 
-        canonical_name = empty_to_null(canonical_name);
-        canonical_type = empty_to_null(canonical_type);
+        const char *canonical_name = empty_to_null(reply.canonical.name);
+        const char *canonical_type = empty_to_null(reply.canonical.type);
+        const char *canonical_domain = reply.canonical.domain;
 
         if (!streq_ptr(name, canonical_name) ||
             !streq_ptr(type, canonical_type) ||
@@ -1025,7 +956,7 @@ static int resolve_service(sd_bus *bus, const char *name, const char *type, cons
                 printf("%s)\n", canonical_domain);
         }
 
-        print_source(flags, ts);
+        print_source(reply.flags, ts);
 
         return 0;
 }
@@ -1033,22 +964,15 @@ static int resolve_service(sd_bus *bus, const char *name, const char *type, cons
 VERB(verb_service, "service", "[[NAME] TYPE] DOMAIN", 2, 4, 0,
      "Resolve service (SRV)");
 static int verb_service(int argc, char *argv[], uintptr_t _data, void *userdata) {
-        _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL;
-        int r;
-
-        r = acquire_bus(&bus);
-        if (r < 0)
-                return r;
-
         if (sd_json_format_enabled(arg_json_format_flags))
                 return log_error_errno(SYNTHETIC_ERRNO(EOPNOTSUPP), "Use --json=pretty with --type= to acquire resource record information in JSON format.");
 
         if (argc == 2)
-                return resolve_service(bus, NULL, NULL, argv[1]);
+                return resolve_service(NULL, NULL, argv[1]);
         else if (argc == 3)
-                return resolve_service(bus, NULL, argv[1], argv[2]);
+                return resolve_service(NULL, argv[1], argv[2]);
         else
-                return resolve_service(bus, argv[1], argv[2], argv[3]);
+                return resolve_service(argv[1], argv[2], argv[3]);
 }
 
 #if HAVE_OPENSSL
index c3a638f3ad2f49cfcd60ce6901a4ce1f870a8b0f..779770a523c18158509fff84326931d5079c007e 100644 (file)
@@ -6,6 +6,7 @@
 #include "iovec-util.h"
 #include "json-util.h"
 #include "resolve-varlink-util.h"
+#include "strv.h"
 
 void resolve_error_done(ResolveError *error) {
         if (!error)
@@ -247,3 +248,129 @@ int dispatch_resolve_record_reply(const char *name, sd_json_variant *variant, sd
         *ret = TAKE_STRUCT(reply);
         return 0;
 }
+
+static void resolved_service_done(ResolvedService *service) {
+        if (!service)
+                return;
+
+        service->hostname = mfree(service->hostname);
+        service->canonical_name = mfree(service->canonical_name);
+        service->addresses = mfree(service->addresses);
+        service->n_addresses = 0;
+}
+
+static int dispatch_resolved_service_address_array(const char *name, sd_json_variant *variant, sd_json_dispatch_flags_t flags, void *userdata) {
+        ResolvedService *service = ASSERT_PTR(userdata);
+        int r;
+
+        sd_json_variant *v;
+        JSON_VARIANT_ARRAY_FOREACH(v, variant) {
+                if (!GREEDY_REALLOC0(service->addresses, service->n_addresses + 1))
+                        return json_log_oom(variant, flags);
+
+                r = dispatch_resolved_address(name, v, flags, &service->addresses[service->n_addresses++]);
+                if (r < 0)
+                        return r;
+        }
+
+        return 0;
+}
+
+static int dispatch_resolved_service(const char *name, sd_json_variant *variant, sd_json_dispatch_flags_t flags, void *userdata) {
+        static const sd_json_dispatch_field resolved_service_dispatch_table[] = {
+                { "priority",      _SD_JSON_VARIANT_TYPE_INVALID, sd_json_dispatch_uint16,                 offsetof(ResolvedService, priority),       SD_JSON_MANDATORY },
+                { "weight",        _SD_JSON_VARIANT_TYPE_INVALID, sd_json_dispatch_uint16,                 offsetof(ResolvedService, weight),         SD_JSON_MANDATORY },
+                { "port",          _SD_JSON_VARIANT_TYPE_INVALID, sd_json_dispatch_uint16,                 offsetof(ResolvedService, port),           SD_JSON_MANDATORY },
+                { "hostname",      SD_JSON_VARIANT_STRING,        sd_json_dispatch_string,                 offsetof(ResolvedService, hostname),       SD_JSON_MANDATORY },
+                { "canonicalName", SD_JSON_VARIANT_STRING,        sd_json_dispatch_string,                 offsetof(ResolvedService, canonical_name), SD_JSON_NULLABLE  },
+                { "addresses",     SD_JSON_VARIANT_ARRAY,         dispatch_resolved_service_address_array, 0,                                         SD_JSON_NULLABLE  },
+                {},
+        };
+        ResolvedService *ret = ASSERT_PTR(userdata);
+        int r;
+
+        _cleanup_(resolved_service_done) ResolvedService service = {};
+        r = sd_json_dispatch(variant, resolved_service_dispatch_table, flags & ~SD_JSON_MANDATORY, &service);
+        if (r < 0)
+                return r;
+
+        *ret = TAKE_STRUCT(service);
+        return 0;
+}
+
+static int dispatch_resolved_service_array(const char *name, sd_json_variant *variant, sd_json_dispatch_flags_t flags, void *userdata) {
+        ResolveServiceReply *reply = ASSERT_PTR(userdata);
+        int r;
+
+        sd_json_variant *v;
+        JSON_VARIANT_ARRAY_FOREACH(v, variant) {
+                if (!GREEDY_REALLOC0(reply->services, reply->n_services + 1))
+                        return json_log_oom(variant, flags);
+
+                r = dispatch_resolved_service(name, v, flags, &reply->services[reply->n_services++]);
+                if (r < 0)
+                        return r;
+        }
+
+        return 0;
+}
+
+static void resolved_canonical_done(ResolvedCanonical *canonical) {
+        if (!canonical)
+                return;
+
+        canonical->name = mfree(canonical->name);
+        canonical->type = mfree(canonical->type);
+        canonical->domain = mfree(canonical->domain);
+}
+
+static int dispatch_resolved_canonical(const char *name, sd_json_variant *variant, sd_json_dispatch_flags_t flags, void *userdata) {
+        static const sd_json_dispatch_field resolved_canonical_dispatch_table[] = {
+                { "name",   SD_JSON_VARIANT_STRING, sd_json_dispatch_string, offsetof(ResolvedCanonical, name),   SD_JSON_NULLABLE  },
+                { "type",   SD_JSON_VARIANT_STRING, sd_json_dispatch_string, offsetof(ResolvedCanonical, type),   SD_JSON_MANDATORY },
+                { "domain", SD_JSON_VARIANT_STRING, sd_json_dispatch_string, offsetof(ResolvedCanonical, domain), SD_JSON_MANDATORY },
+                {},
+        };
+        ResolvedCanonical *ret = ASSERT_PTR(userdata);
+        int r;
+
+        _cleanup_(resolved_canonical_done) ResolvedCanonical canonical = {};
+        r = sd_json_dispatch(variant, resolved_canonical_dispatch_table, flags & ~SD_JSON_MANDATORY, &canonical);
+        if (r < 0)
+                return r;
+
+        *ret = TAKE_STRUCT(canonical);
+        return 0;
+}
+
+void resolve_service_reply_done(ResolveServiceReply *reply) {
+        if (!reply)
+                return;
+
+        FOREACH_ARRAY(service, reply->services, reply->n_services)
+                resolved_service_done(service);
+        reply->services = mfree(reply->services);
+        reply->n_services = 0;
+        reply->txt = strv_free(reply->txt);
+        resolved_canonical_done(&reply->canonical);
+}
+
+int dispatch_resolve_service_reply(const char *name, sd_json_variant *variant, sd_json_dispatch_flags_t flags, void *userdata) {
+        static const sd_json_dispatch_field resolve_service_reply_dispatch_table[] = {
+                { "services",  SD_JSON_VARIANT_ARRAY,         dispatch_resolved_service_array, 0,                                        SD_JSON_MANDATORY },
+                { "txt",       SD_JSON_VARIANT_ARRAY,         sd_json_dispatch_strv,           offsetof(ResolveServiceReply, txt),       SD_JSON_NULLABLE  },
+                { "canonical", SD_JSON_VARIANT_OBJECT,        dispatch_resolved_canonical,     offsetof(ResolveServiceReply, canonical), SD_JSON_MANDATORY },
+                { "flags",     _SD_JSON_VARIANT_TYPE_INVALID, sd_json_dispatch_uint64,         offsetof(ResolveServiceReply, flags),     SD_JSON_MANDATORY },
+                {},
+        };
+        ResolveServiceReply *ret = ASSERT_PTR(userdata);
+        int r;
+
+        _cleanup_(resolve_service_reply_done) ResolveServiceReply reply = {};
+        r = sd_json_dispatch(variant, resolve_service_reply_dispatch_table, flags | SD_JSON_ALLOW_EXTENSIONS, &reply);
+        if (r < 0)
+                return r;
+
+        *ret = TAKE_STRUCT(reply);
+        return 0;
+}
index 58e8f4d9184f63cd5e3131eabf576f8d4876b128..a05e18daea516761cfbe109b54e6256a2217c9b6 100644 (file)
@@ -64,3 +64,31 @@ typedef struct ResolveRecordReply {
 void resolve_record_reply_done(ResolveRecordReply *reply);
 
 int dispatch_resolve_record_reply(const char *name, sd_json_variant *variant, sd_json_dispatch_flags_t flags, void *userdata);
+
+typedef struct ResolvedService {
+        uint16_t priority;
+        uint16_t weight;
+        uint16_t port;
+        char *hostname;
+        char *canonical_name;
+        ResolvedAddress *addresses;
+        size_t n_addresses;
+} ResolvedService;
+
+typedef struct ResolvedCanonical {
+        char *name;
+        char *type;
+        char *domain;
+} ResolvedCanonical;
+
+typedef struct ResolveServiceReply {
+        ResolvedService *services;
+        size_t n_services;
+        char **txt;
+        ResolvedCanonical canonical;
+        uint64_t flags;
+} ResolveServiceReply;
+
+void resolve_service_reply_done(ResolveServiceReply *reply);
+
+int dispatch_resolve_service_reply(const char *name, sd_json_variant *variant, sd_json_dispatch_flags_t flags, void *userdata);