diff --git a/src/network/networkd-queue.c b/src/network/networkd-queue.c index d2a2b831ab..d0cb61d59f 100644 --- a/src/network/networkd-queue.c +++ b/src/network/networkd-queue.c @@ -22,6 +22,7 @@ static Request* request_detach_impl(Request *req) { ordered_set_remove(req->manager->request_queue, req); req->manager = NULL; + req->netlink_slot = sd_netlink_slot_unref(req->netlink_slot); return req; } @@ -328,6 +329,10 @@ int manager_process_requests(Manager *manager) { static int request_netlink_handler(sd_netlink *nl, sd_netlink_message *m, Request *req) { assert(req); + assert(req->netlink_slot); + assert_se(sd_netlink_slot_set_floating(req->netlink_slot, true) >= 0); + req->netlink_slot = sd_netlink_slot_unref(req->netlink_slot); + if (req->counter) { assert(*req->counter > 0); (*req->counter)--; @@ -350,7 +355,7 @@ int request_call_netlink_async(sd_netlink *nl, sd_netlink_message *m, Request *r assert(m); assert(req); - r = netlink_call_async(nl, NULL, m, request_netlink_handler, request_destroy_callback, req); + r = netlink_call_async(nl, &req->netlink_slot, m, request_netlink_handler, request_destroy_callback, req); if (r < 0) return r; diff --git a/src/network/networkd-queue.h b/src/network/networkd-queue.h index 172ff230b4..5f33a1afeb 100644 --- a/src/network/networkd-queue.h +++ b/src/network/networkd-queue.h @@ -80,6 +80,7 @@ typedef struct Request { * request, and pass this request to the netlink_call_async(), and set the destroy function * to the slot. */ request_netlink_handler_t netlink_handler; + sd_netlink_slot *netlink_slot; bool waiting_reply; } Request; diff --git a/src/network/test-network.c b/src/network/test-network.c index ee0e5d13b5..d91f256164 100644 --- a/src/network/test-network.c +++ b/src/network/test-network.c @@ -1,11 +1,18 @@ /* SPDX-License-Identifier: LGPL-2.1-or-later */ +#include + +#include "sd-netlink.h" + #include "alloc-util.h" #include "hashmap.h" #include "hostname-setup.h" +#include "netlink-util.h" #include "network-internal.h" #include "networkd-manager.h" +#include "networkd-queue.h" #include "networkd-route-util.h" +#include "ordered-set.h" #include "strv.h" #include "tests.h" #include "vrf.h" @@ -126,6 +133,80 @@ TEST(vrf_table) { ASSERT_EQ(vrf.table, 5678U); } +static int test_request_process(Request *req, Link *link, void *userdata) { + assert_not_reached(); +} + +static int test_request_netlink_handler( + sd_netlink *rtnl, + sd_netlink_message *message, + Request *req, + Link *link, + void *userdata) { + + assert(rtnl); + assert(message); + assert(req); + assert(!link); + assert(userdata); + + *(bool*) userdata = true; + return 0; +} + +static void test_request_netlink_handler_one(bool detach) { + _cleanup_(manager_freep) Manager *manager = NULL; + _cleanup_(sd_netlink_unrefp) sd_netlink *rtnl = NULL; + _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *message = NULL; + bool handler_called = false; + unsigned counter = 0; + Request *req; + + ASSERT_OK(manager_new(&manager, /* test_mode= */ true)); + ASSERT_OK(sd_netlink_open(&rtnl)); + + ASSERT_OK_POSITIVE(manager_queue_request_full( + manager, + REQUEST_TYPE_ADDRESS_LABEL, + &handler_called, + /* free_func= */ NULL, + /* hash_func= */ NULL, + /* compare_func= */ NULL, + test_request_process, + &counter, + test_request_netlink_handler, + &req)); + ASSERT_EQ(counter, 1U); + + int ifindex = (int) if_nametoindex("lo"); + ASSERT_GT(ifindex, 0); + ASSERT_OK(sd_rtnl_message_new_link(rtnl, &message, RTM_GETLINK, ifindex)); + ASSERT_OK(request_call_netlink_async(rtnl, message, req)); + ASSERT_EQ(netlink_get_reply_callback_count(rtnl), 1U); + + if (detach) { + request_detach(req); + ASSERT_EQ(counter, 0U); + ASSERT_EQ(netlink_get_reply_callback_count(rtnl), 0U); + } + + ASSERT_OK(sd_netlink_wait(rtnl, 0)); + ASSERT_OK_POSITIVE(sd_netlink_process(rtnl, /* ret= */ NULL)); + + ASSERT_EQ(handler_called, !detach); + ASSERT_EQ(counter, 0U); + ASSERT_EQ(netlink_get_reply_callback_count(rtnl), 0U); + ASSERT_TRUE(ordered_set_isempty(manager->request_queue)); +} + +TEST(request_netlink_handler_called) { + test_request_netlink_handler_one(/* detach= */ false); +} + +TEST(request_netlink_handler_detached) { + test_request_netlink_handler_one(/* detach= */ true); +} + TEST(manager_enumerate) { _cleanup_(manager_freep) Manager *manager = NULL;