diff options
author | Lennart Poettering <lennart@poettering.net> | 2018-11-06 19:30:59 +0300 |
---|---|---|
committer | GitHub <noreply@github.com> | 2018-11-06 19:30:59 +0300 |
commit | b992109b3ed639854cfb58a08dab3524e349eeb6 (patch) | |
tree | 538f82c590aa8e5bbfee25b2a1e613da17408a9a | |
parent | ed2e7967befb9074df92d0b8dab3863133e11462 (diff) | |
parent | a7cc45caadec7b113ecc9f8603bfe3387dfefbf8 (diff) | |
download | systemd-b992109b3ed639854cfb58a08dab3524e349eeb6.tar.gz systemd-b992109b3ed639854cfb58a08dab3524e349eeb6.tar.bz2 systemd-b992109b3ed639854cfb58a08dab3524e349eeb6.zip |
Merge pull request #10633 from yuwata/sd-resolve-destroy
Another solution to fix wireguard issues
-rw-r--r-- | src/libsystemd/libsystemd.sym | 3 | ||||
-rw-r--r-- | src/libsystemd/sd-event/sd-event.c | 28 | ||||
-rw-r--r-- | src/libsystemd/sd-resolve/sd-resolve.c | 48 | ||||
-rw-r--r-- | src/network/netdev/bridge.c | 2 | ||||
-rw-r--r-- | src/network/netdev/geneve.c | 2 | ||||
-rw-r--r-- | src/network/netdev/netdev.c | 16 | ||||
-rw-r--r-- | src/network/netdev/netdev.h | 2 | ||||
-rw-r--r-- | src/network/netdev/wireguard.c | 130 | ||||
-rw-r--r-- | src/network/netdev/wireguard.h | 7 | ||||
-rw-r--r-- | src/network/networkd-link.c | 20 | ||||
-rw-r--r-- | src/network/networkd-manager.c | 16 | ||||
-rw-r--r-- | src/systemd/sd-event.h | 2 | ||||
-rw-r--r-- | src/systemd/sd-resolve.h | 5 |
13 files changed, 220 insertions, 61 deletions
diff --git a/src/libsystemd/libsystemd.sym b/src/libsystemd/libsystemd.sym index 8d46081ec4..1f2238ca37 100644 --- a/src/libsystemd/libsystemd.sym +++ b/src/libsystemd/libsystemd.sym @@ -663,4 +663,7 @@ global: sd_device_monitor_filter_add_match_tag; sd_device_monitor_filter_update; sd_device_monitor_filter_remove; + + sd_event_source_get_floating; + sd_event_source_set_floating; } LIBSYSTEMD_239; diff --git a/src/libsystemd/sd-event/sd-event.c b/src/libsystemd/sd-event/sd-event.c index f44e6b4cca..27caa8681c 100644 --- a/src/libsystemd/sd-event/sd-event.c +++ b/src/libsystemd/sd-event/sd-event.c @@ -3702,3 +3702,31 @@ _public_ int sd_event_source_get_destroy_callback(sd_event_source *s, sd_event_d return !!s->destroy_callback; } + +_public_ int sd_event_source_get_floating(sd_event_source *s) { + assert_return(s, -EINVAL); + + return s->floating; +} + +_public_ int sd_event_source_set_floating(sd_event_source *s, int b) { + assert_return(s, -EINVAL); + + if (s->floating == !!b) + return 0; + + if (!s->event) /* Already disconnected */ + return -ESTALE; + + s->floating = b; + + if (b) { + sd_event_source_ref(s); + sd_event_unref(s->event); + } else { + sd_event_ref(s->event); + sd_event_source_unref(s); + } + + return 1; +} diff --git a/src/libsystemd/sd-resolve/sd-resolve.c b/src/libsystemd/sd-resolve/sd-resolve.c index 61876781c0..1fbab9dbf9 100644 --- a/src/libsystemd/sd-resolve/sd-resolve.c +++ b/src/libsystemd/sd-resolve/sd-resolve.c @@ -94,6 +94,7 @@ struct sd_resolve_query { }; void *userdata; + sd_resolve_destroy_t destroy_callback; LIST_FIELDS(sd_resolve_query, queries); }; @@ -1095,6 +1096,9 @@ static sd_resolve_query *resolve_query_free(sd_resolve_query *q) { resolve_query_disconnect(q); + if (q->destroy_callback) + q->destroy_callback(q->userdata); + resolve_freeaddrinfo(q->addrinfo); free(q->host); free(q->serv); @@ -1137,6 +1141,50 @@ _public_ sd_resolve *sd_resolve_query_get_resolve(sd_resolve_query *q) { return q->resolve; } +_public_ int sd_resolve_query_get_destroy_callback(sd_resolve_query *q, sd_resolve_destroy_t *destroy_callback) { + assert_return(q, -EINVAL); + + if (destroy_callback) + *destroy_callback = q->destroy_callback; + + return !!q->destroy_callback; +} + +_public_ int sd_resolve_query_set_destroy_callback(sd_resolve_query *q, sd_resolve_destroy_t destroy_callback) { + assert_return(q, -EINVAL); + + q->destroy_callback = destroy_callback; + return 0; +} + +_public_ int sd_resolve_query_get_floating(sd_resolve_query *q) { + assert_return(q, -EINVAL); + + return q->floating; +} + +_public_ int sd_resolve_query_set_floating(sd_resolve_query *q, int b) { + assert_return(q, -EINVAL); + + if (q->floating == !!b) + return 0; + + if (!q->resolve) /* Already disconnected */ + return -ESTALE; + + q->floating = b; + + if (b) { + sd_resolve_query_ref(q); + sd_resolve_unref(q->resolve); + } else { + sd_resolve_ref(q->resolve); + sd_resolve_query_unref(q); + } + + return 1; +} + static int io_callback(sd_event_source *s, int fd, uint32_t revents, void *userdata) { sd_resolve *resolve = userdata; int r; diff --git a/src/network/netdev/bridge.c b/src/network/netdev/bridge.c index 5a462f8376..cb00780b8a 100644 --- a/src/network/netdev/bridge.c +++ b/src/network/netdev/bridge.c @@ -130,7 +130,7 @@ static int netdev_bridge_post_create(NetDev *netdev, Link *link, sd_netlink_mess return log_netdev_error_errno(netdev, r, "Could not append IFLA_INFO_DATA attribute: %m"); r = sd_netlink_call_async(netdev->manager->rtnl, NULL, req, netdev_bridge_set_handler, - netdev_netlink_destroy_callback, netdev, 0, __func__); + netdev_destroy_callback, netdev, 0, __func__); if (r < 0) return log_netdev_error_errno(netdev, r, "Could not send rtnetlink message: %m"); diff --git a/src/network/netdev/geneve.c b/src/network/netdev/geneve.c index e407a8fc5c..1742e399b8 100644 --- a/src/network/netdev/geneve.c +++ b/src/network/netdev/geneve.c @@ -137,7 +137,7 @@ static int netdev_geneve_create(NetDev *netdev) { return log_netdev_error_errno(netdev, r, "Could not append IFLA_LINKINFO attribute: %m"); r = sd_netlink_call_async(netdev->manager->rtnl, NULL, m, geneve_netdev_create_handler, - netdev_netlink_destroy_callback, netdev, 0, __func__); + netdev_destroy_callback, netdev, 0, __func__); if (r < 0) return log_netdev_error_errno(netdev, r, "Could not send rtnetlink message: %m"); diff --git a/src/network/netdev/netdev.c b/src/network/netdev/netdev.c index 6eb63355a4..52b40dd68e 100644 --- a/src/network/netdev/netdev.c +++ b/src/network/netdev/netdev.c @@ -112,13 +112,19 @@ static void netdev_callbacks_clear(NetDev *netdev) { } } +static void netdev_detach_from_manager(NetDev *netdev) { + if (netdev->ifname && netdev->manager) + hashmap_remove(netdev->manager->netdevs, netdev->ifname); + + netdev->manager = NULL; +} + static NetDev *netdev_free(NetDev *netdev) { assert(netdev); netdev_callbacks_clear(netdev); - if (netdev->ifname && netdev->manager) - hashmap_remove(netdev->manager->netdevs, netdev->ifname); + netdev_detach_from_manager(netdev); free(netdev->filename); @@ -149,7 +155,7 @@ static NetDev *netdev_free(NetDev *netdev) { DEFINE_TRIVIAL_REF_UNREF_FUNC(NetDev, netdev, netdev_free); -void netdev_netlink_destroy_callback(void *userdata) { +void netdev_destroy_callback(void *userdata) { NetDev *netdev = userdata; assert(userdata); @@ -167,6 +173,8 @@ void netdev_drop(NetDev *netdev) { netdev_callbacks_clear(netdev); + netdev_detach_from_manager(netdev); + netdev_unref(netdev); return; @@ -542,7 +550,7 @@ static int netdev_create(NetDev *netdev, Link *link, link_ref(link); } else { r = sd_netlink_call_async(netdev->manager->rtnl, NULL, m, netdev_create_handler, - netdev_netlink_destroy_callback, netdev, 0, __func__); + netdev_destroy_callback, netdev, 0, __func__); if (r < 0) return log_netdev_error_errno(netdev, r, "Could not send rtnetlink message: %m"); diff --git a/src/network/netdev/netdev.h b/src/network/netdev/netdev.h index 2c1a59f2c4..8c884bb124 100644 --- a/src/network/netdev/netdev.h +++ b/src/network/netdev/netdev.h @@ -150,7 +150,7 @@ void netdev_drop(NetDev *netdev); NetDev *netdev_unref(NetDev *netdev); NetDev *netdev_ref(NetDev *netdev); -void netdev_netlink_destroy_callback(void *userdata); +void netdev_destroy_callback(void *userdata); DEFINE_TRIVIAL_CLEANUP_FUNC(NetDev*, netdev_unref); int netdev_get(Manager *manager, const char *name, NetDev **ret); diff --git a/src/network/netdev/wireguard.c b/src/network/netdev/wireguard.c index 1cab31302e..d5a0a19e9f 100644 --- a/src/network/netdev/wireguard.c +++ b/src/network/netdev/wireguard.c @@ -6,6 +6,8 @@ #include <sys/ioctl.h> #include <net/if.h> +#include "sd-resolve.h" + #include "alloc-util.h" #include "parse-util.h" #include "fd-util.h" @@ -28,10 +30,13 @@ static WireguardPeer *wireguard_peer_new(Wireguard *w, unsigned section) { if (w->last_peer_section == section && w->peers) return w->peers; - peer = new0(WireguardPeer, 1); + peer = new(WireguardPeer, 1); if (!peer) return NULL; - peer->flags = WGPEER_F_REPLACE_ALLOWEDIPS; + + *peer = (WireguardPeer) { + .flags = WGPEER_F_REPLACE_ALLOWEDIPS, + }; LIST_PREPEND(peers, w->peers, peer); w->last_peer_section = section; @@ -195,12 +200,21 @@ static int set_wireguard_interface(NetDev *netdev) { static WireguardEndpoint* wireguard_endpoint_free(WireguardEndpoint *e) { if (!e) return NULL; - netdev_unref(e->netdev); e->host = mfree(e->host); e->port = mfree(e->port); return mfree(e); } +static void wireguard_endpoint_destroy_callback(void *userdata) { + WireguardEndpoint *e = userdata; + + assert(e); + assert(e->netdev); + + netdev_unref(e->netdev); + wireguard_endpoint_free(e); +} + DEFINE_TRIVIAL_CLEANUP_FUNC(WireguardEndpoint*, wireguard_endpoint_free); static int on_resolve_retry(sd_event_source *s, usec_t usec, void *userdata) { @@ -211,8 +225,11 @@ static int on_resolve_retry(sd_event_source *s, usec_t usec, void *userdata) { w = WIREGUARD(netdev); assert(w); - w->resolve_retry_event_source = sd_event_source_unref(w->resolve_retry_event_source); + if (!netdev->manager) + /* The netdev is detached. */ + return 0; + assert(!w->unresolved_endpoints); w->unresolved_endpoints = TAKE_PTR(w->failed_endpoints); resolve_endpoints(netdev); @@ -232,9 +249,10 @@ static int wireguard_resolve_handler(sd_resolve_query *q, int ret, const struct addrinfo *ai, void *userdata) { - NetDev *netdev; + _cleanup_(netdev_unrefp) NetDev *netdev_will_unrefed = NULL; + NetDev *netdev = NULL; + WireguardEndpoint *e; Wireguard *w; - _cleanup_(wireguard_endpoint_freep) WireguardEndpoint *e; int r; assert(userdata); @@ -245,14 +263,17 @@ static int wireguard_resolve_handler(sd_resolve_query *q, w = WIREGUARD(netdev); assert(w); - w->resolve_query = sd_resolve_query_unref(w->resolve_query); + if (!netdev->manager) + /* The netdev is detached. */ + return 0; if (ret != 0) { log_netdev_error(netdev, "Failed to resolve host '%s:%s': %s", e->host, e->port, gai_strerror(ret)); LIST_PREPEND(endpoints, w->failed_endpoints, e); - e = NULL; + (void) sd_resolve_query_set_destroy_callback(q, NULL); /* Avoid freeing endpoint by destroy callback. */ + netdev_will_unrefed = netdev; /* But netdev needs to be unrefed. */ } else if ((ai->ai_family == AF_INET && ai->ai_addrlen == sizeof(struct sockaddr_in)) || - (ai->ai_family == AF_INET6 && ai->ai_addrlen == sizeof(struct sockaddr_in6))) + (ai->ai_family == AF_INET6 && ai->ai_addrlen == sizeof(struct sockaddr_in6))) memcpy(&e->peer->endpoint, ai->ai_addr, ai->ai_addrlen); else log_netdev_error(netdev, "Neither IPv4 nor IPv6 address found for peer endpoint: %s:%s", e->host, e->port); @@ -264,38 +285,53 @@ static int wireguard_resolve_handler(sd_resolve_query *q, set_wireguard_interface(netdev); if (w->failed_endpoints) { + _cleanup_(sd_event_source_unrefp) sd_event_source *s = NULL; + w->n_retries++; r = sd_event_add_time(netdev->manager->event, - &w->resolve_retry_event_source, + &s, CLOCK_MONOTONIC, now(CLOCK_MONOTONIC) + exponential_backoff_milliseconds(w->n_retries), 0, on_resolve_retry, netdev); - if (r < 0) + if (r < 0) { log_netdev_warning_errno(netdev, r, "Could not arm resolve retry handler: %m"); + return 0; + } + + r = sd_event_source_set_destroy_callback(s, netdev_destroy_callback); + if (r < 0) { + log_netdev_warning_errno(netdev, r, "Failed to set destroy callback to event source: %m"); + return 0; + } + + (void) sd_event_source_set_floating(s, true); + netdev_ref(netdev); } return 0; } static void resolve_endpoints(NetDev *netdev) { - int r = 0; - Wireguard *w; - WireguardEndpoint *endpoint; static const struct addrinfo hints = { .ai_family = AF_UNSPEC, .ai_socktype = SOCK_DGRAM, .ai_protocol = IPPROTO_UDP }; + WireguardEndpoint *endpoint; + Wireguard *w; + int r = 0; assert(netdev); w = WIREGUARD(netdev); assert(w); LIST_FOREACH(endpoints, endpoint, w->unresolved_endpoints) { + _cleanup_(sd_resolve_query_unrefp) sd_resolve_query *q = NULL; + r = sd_resolve_getaddrinfo(netdev->manager->resolve, - &w->resolve_query, + &q, endpoint->host, endpoint->port, &hints, @@ -304,11 +340,23 @@ static void resolve_endpoints(NetDev *netdev) { if (r == -ENOBUFS) break; + if (r < 0) { + log_netdev_error_errno(netdev, r, "Failed to create resolver: %m"); + continue; + } - LIST_REMOVE(endpoints, w->unresolved_endpoints, endpoint); + r = sd_resolve_query_set_destroy_callback(q, wireguard_endpoint_destroy_callback); + if (r < 0) { + log_netdev_error_errno(netdev, r, "Failed to set destroy callback to resolving query: %m"); + continue; + } - if (r < 0) - log_netdev_error_errno(netdev, r, "Failed create resolver: %m"); + (void) sd_resolve_query_set_floating(q, true); + + /* Avoid freeing netdev. It will be unrefed by the destroy callback. */ + netdev_ref(netdev); + + LIST_REMOVE(endpoints, w->unresolved_endpoints, endpoint); } } @@ -531,12 +579,15 @@ int config_parse_wireguard_allowed_ips(const char *unit, return 0; } - ipmask = new0(WireguardIPmask, 1); + ipmask = new(WireguardIPmask, 1); if (!ipmask) return log_oom(); - ipmask->family = family; - ipmask->ip.in6 = addr.in6; - ipmask->cidr = prefixlen; + + *ipmask = (WireguardIPmask) { + .family = family, + .ip.in6 = addr.in6, + .cidr = prefixlen, + }; LIST_PREPEND(ipmasks, peer->ipmasks, ipmask); } @@ -572,10 +623,6 @@ int config_parse_wireguard_endpoint(const char *unit, if (!peer) return log_oom(); - endpoint = new0(WireguardEndpoint, 1); - if (!endpoint) - return log_oom(); - if (rvalue[0] == '[') { begin = &rvalue[1]; end = strchr(rvalue, ']'); @@ -609,12 +656,17 @@ int config_parse_wireguard_endpoint(const char *unit, if (!port) return log_oom(); - endpoint->peer = TAKE_PTR(peer); - endpoint->host = TAKE_PTR(host); - endpoint->port = TAKE_PTR(port); - endpoint->netdev = netdev_ref(data); - LIST_PREPEND(endpoints, w->unresolved_endpoints, endpoint); - endpoint = NULL; + endpoint = new(WireguardEndpoint, 1); + if (!endpoint) + return log_oom(); + + *endpoint = (WireguardEndpoint) { + .peer = TAKE_PTR(peer), + .host = TAKE_PTR(host), + .port = TAKE_PTR(port), + .netdev = data, + }; + LIST_PREPEND(endpoints, w->unresolved_endpoints, TAKE_PTR(endpoint)); return 0; } @@ -673,11 +725,11 @@ static void wireguard_done(NetDev *netdev) { Wireguard *w; WireguardPeer *peer; WireguardIPmask *mask; + WireguardEndpoint *e; assert(netdev); w = WIREGUARD(netdev); - assert(!w->unresolved_endpoints); - w->resolve_retry_event_source = sd_event_source_unref(w->resolve_retry_event_source); + assert(w); while ((peer = w->peers)) { LIST_REMOVE(peers, w->peers, peer); @@ -687,6 +739,16 @@ static void wireguard_done(NetDev *netdev) { } free(peer); } + + while ((e = w->unresolved_endpoints)) { + LIST_REMOVE(endpoints, w->unresolved_endpoints, e); + wireguard_endpoint_free(e); + } + + while ((e = w->failed_endpoints)) { + LIST_REMOVE(endpoints, w->failed_endpoints, e); + wireguard_endpoint_free(e); + } } const NetDevVTable wireguard_vtable = { diff --git a/src/network/netdev/wireguard.h b/src/network/netdev/wireguard.h index 80a5bf87a0..bd97004519 100644 --- a/src/network/netdev/wireguard.h +++ b/src/network/netdev/wireguard.h @@ -2,11 +2,10 @@ typedef struct Wireguard Wireguard; +#include "in-addr-util.h" #include "netdev.h" -#include "sd-resolve.h" -#include "wireguard-netlink.h" #include "socket-util.h" -#include "in-addr-util.h" +#include "wireguard-netlink.h" #ifndef IFNAMSIZ #define IFNAMSIZ 16 @@ -58,12 +57,10 @@ struct Wireguard { LIST_HEAD(WireguardPeer, peers); size_t allocation_size; - sd_event_source *resolve_retry_event_source; LIST_HEAD(WireguardEndpoint, unresolved_endpoints); LIST_HEAD(WireguardEndpoint, failed_endpoints); unsigned n_retries; - sd_resolve_query *resolve_query; }; DEFINE_NETDEV_CAST(WIREGUARD, Wireguard); diff --git a/src/network/networkd-link.c b/src/network/networkd-link.c index 2c0c04985d..1b233507e7 100644 --- a/src/network/networkd-link.c +++ b/src/network/networkd-link.c @@ -503,6 +503,17 @@ static int link_new(Manager *manager, sd_netlink_message *message, Link **ret) { return 0; } +static void link_detach_from_manager(Link *link) { + if (!link || !link->manager) + return; + + hashmap_remove(link->manager->links, INT_TO_PTR(link->ifindex)); + set_remove(link->manager->links_requesting_uuid, link); + link_clean(link); + + link->manager = NULL; +} + static Link *link_free(Link *link) { Address *address; Link *carrier; @@ -552,11 +563,7 @@ static Link *link_free(Link *link) { sd_ndisc_unref(link->ndisc); sd_radv_unref(link->radv); - if (link->manager) { - hashmap_remove(link->manager->links, INT_TO_PTR(link->ifindex)); - set_remove(link->manager->links_requesting_uuid, link); - link_clean(link); - } + link_detach_from_manager(link); free(link->ifname); @@ -2265,6 +2272,9 @@ void link_drop(Link *link) { log_link_debug(link, "Link removed"); (void) unlink(link->state_file); + + link_detach_from_manager(link); + link_unref(link); return; diff --git a/src/network/networkd-manager.c b/src/network/networkd-manager.c index c8a6f81fd8..69861a680e 100644 --- a/src/network/networkd-manager.c +++ b/src/network/networkd-manager.c @@ -1407,10 +1407,9 @@ int manager_new(Manager **ret) { } void manager_free(Manager *m) { + AddressPool *pool; Network *network; - NetDev *netdev; Link *link; - AddressPool *pool; if (!m) return; @@ -1419,6 +1418,7 @@ void manager_free(Manager *m) { sd_netlink_unref(m->rtnl); sd_netlink_unref(m->genl); + sd_resolve_unref(m->resolve); while ((network = m->networks)) network_free(network); @@ -1437,16 +1437,14 @@ void manager_free(Manager *m) { link_unref(link); } - set_free_with_destructor(m->dirty_links, link_unref); - hashmap_free(m->links); - set_free(m->links_requesting_uuid); + m->dirty_links = set_free_with_destructor(m->dirty_links, link_unref); + m->links = hashmap_free(m->links); + m->links_requesting_uuid = set_free(m->links_requesting_uuid); set_free(m->duids_requesting_uuid); hashmap_free(m->networks_by_name); - while ((netdev = hashmap_first(m->netdevs))) - netdev_unref(netdev); - hashmap_free(m->netdevs); + m->netdevs = hashmap_free_with_destructor(m->netdevs, netdev_unref); while ((pool = m->address_pools)) address_pool_free(pool); @@ -1459,8 +1457,6 @@ void manager_free(Manager *m) { sd_event_unref(m->event); - sd_resolve_unref(m->resolve); - sd_device_monitor_unref(m->device_monitor); sd_bus_unref(m->bus); diff --git a/src/systemd/sd-event.h b/src/systemd/sd-event.h index c38eb84beb..b15cade20a 100644 --- a/src/systemd/sd-event.h +++ b/src/systemd/sd-event.h @@ -143,6 +143,8 @@ int sd_event_source_get_child_pid(sd_event_source *s, pid_t *pid); int sd_event_source_get_inotify_mask(sd_event_source *s, uint32_t *ret); int sd_event_source_set_destroy_callback(sd_event_source *s, sd_event_destroy_t callback); int sd_event_source_get_destroy_callback(sd_event_source *s, sd_event_destroy_t *ret); +int sd_event_source_get_floating(sd_event_source *s); +int sd_event_source_set_floating(sd_event_source *s, int b); /* Define helpers so that __attribute__((cleanup(sd_event_unrefp))) and similar may be used. */ _SD_DEFINE_POINTER_CLEANUP_FUNC(sd_event, sd_event_unref); diff --git a/src/systemd/sd-resolve.h b/src/systemd/sd-resolve.h index 5695119b40..089fcdee37 100644 --- a/src/systemd/sd-resolve.h +++ b/src/systemd/sd-resolve.h @@ -42,6 +42,7 @@ typedef struct sd_resolve_query sd_resolve_query; /* A callback on completion */ typedef int (*sd_resolve_getaddrinfo_handler_t)(sd_resolve_query *q, int ret, const struct addrinfo *ai, void *userdata); typedef int (*sd_resolve_getnameinfo_handler_t)(sd_resolve_query *q, int ret, const char *host, const char *serv, void *userdata); +typedef void (*sd_resolve_destroy_t)(void *userdata); enum { SD_RESOLVE_GET_HOST = 1 << 0, @@ -108,6 +109,10 @@ int sd_resolve_query_is_done(sd_resolve_query*q); void *sd_resolve_query_get_userdata(sd_resolve_query *q); void *sd_resolve_query_set_userdata(sd_resolve_query *q, void *userdata); +int sd_resolve_query_get_destroy_callback(sd_resolve_query *q, sd_resolve_destroy_t *destroy_callback); +int sd_resolve_query_set_destroy_callback(sd_resolve_query *q, sd_resolve_destroy_t destroy_callback); +int sd_resolve_query_get_floating(sd_resolve_query *q); +int sd_resolve_query_set_floating(sd_resolve_query *q, int b); sd_resolve *sd_resolve_query_get_resolve(sd_resolve_query *q); |