Merge pull request #10201 from yuwata/fix-10196

sd-netlink: add destroy_callback to sd_netlink_call_async() and fix memleaks in networkd
This commit is contained in:
Lennart Poettering
2018-10-12 11:36:08 +02:00
committed by GitHub
25 changed files with 350 additions and 234 deletions
@@ -23,6 +23,7 @@ struct reply_callback {
usec_t timeout;
uint64_t serial;
unsigned prioq_idx;
sd_netlink_destroy_t destroy_callback;
};
struct match_callback {
+57 -37
View File
@@ -21,17 +21,23 @@ static int sd_netlink_new(sd_netlink **ret) {
assert_return(ret, -EINVAL);
rtnl = new0(sd_netlink, 1);
rtnl = new(sd_netlink, 1);
if (!rtnl)
return -ENOMEM;
rtnl->n_ref = REFCNT_INIT;
rtnl->fd = -1;
rtnl->sockaddr.nl.nl_family = AF_NETLINK;
rtnl->original_pid = getpid_cached();
rtnl->protocol = -1;
*rtnl = (sd_netlink) {
.n_ref = REFCNT_INIT,
.fd = -1,
.sockaddr.nl.nl_family = AF_NETLINK,
.original_pid = getpid_cached(),
.protocol = -1,
LIST_HEAD_INIT(rtnl->match_callbacks);
/* Change notification responses have sequence 0, so we must
* start our request sequence numbers at 1, or we may confuse our
* responses with notifications from the kernel */
.serial = 1,
};
/* We guarantee that the read buffer has at least space for
* a message header */
@@ -39,11 +45,6 @@ static int sd_netlink_new(sd_netlink **ret) {
sizeof(struct nlmsghdr), sizeof(uint8_t)))
return -ENOMEM;
/* Change notification responses have sequence 0, so we must
* start our request sequence numbers at 1, or we may confuse our
* responses with notifications from the kernel */
rtnl->serial = 1;
*ret = TAKE_PTR(rtnl);
return 0;
@@ -147,6 +148,7 @@ int sd_netlink_inc_rcvbuf(sd_netlink *rtnl, size_t size) {
}
static sd_netlink *netlink_free(sd_netlink *rtnl) {
struct reply_callback *c;
struct match_callback *f;
unsigned i;
@@ -162,7 +164,12 @@ static sd_netlink *netlink_free(sd_netlink *rtnl) {
free(rtnl->rbuffer);
hashmap_free_free(rtnl->reply_callbacks);
while ((c = hashmap_steal_first(rtnl->reply_callbacks))) {
if (c->destroy_callback)
c->destroy_callback(c->userdata);
free(c);
}
hashmap_free(rtnl->reply_callbacks);
prioq_free(rtnl->reply_callbacks_prioq);
sd_event_source_unref(rtnl->io_event_source);
@@ -298,6 +305,9 @@ static int process_timeout(sd_netlink *rtnl) {
if (r < 0)
log_debug_errno(r, "sd-netlink: timedout callback failed: %m");
if (c->destroy_callback)
c->destroy_callback(c->userdata);
free(c);
return 1;
@@ -331,6 +341,9 @@ static int process_reply(sd_netlink *rtnl, sd_netlink_message *m) {
if (r < 0)
log_debug_errno(r, "sd-netlink: callback failed: %m");
if (c->destroy_callback)
c->destroy_callback(c->userdata);
return 1;
}
@@ -493,13 +506,15 @@ static int timeout_compare(const void *a, const void *b) {
return CMP(x->timeout, y->timeout);
}
int sd_netlink_call_async(sd_netlink *nl,
sd_netlink_message *m,
sd_netlink_message_handler_t callback,
void *userdata,
uint64_t usec,
uint32_t *serial) {
struct reply_callback *c;
int sd_netlink_call_async(
sd_netlink *nl,
sd_netlink_message *m,
sd_netlink_message_handler_t callback,
sd_netlink_destroy_t destroy_callback,
void *userdata,
uint64_t usec,
uint32_t *serial) {
_cleanup_free_ struct reply_callback *c = NULL;
uint32_t s;
int r, k;
@@ -518,33 +533,31 @@ int sd_netlink_call_async(sd_netlink *nl,
return r;
}
c = new0(struct reply_callback, 1);
c = new(struct reply_callback, 1);
if (!c)
return -ENOMEM;
c->callback = callback;
c->userdata = userdata;
c->timeout = calc_elapse(usec);
*c = (struct reply_callback) {
.callback = callback,
.userdata = userdata,
.timeout = calc_elapse(usec),
.destroy_callback = destroy_callback,
};
k = sd_netlink_send(nl, m, &s);
if (k < 0) {
free(c);
if (k < 0)
return k;
}
c->serial = s;
r = hashmap_put(nl->reply_callbacks, &c->serial, c);
if (r < 0) {
free(c);
if (r < 0)
return r;
}
if (c->timeout != 0) {
r = prioq_put(nl->reply_callbacks_prioq, c, &c->prioq_idx);
if (r > 0) {
c->timeout = 0;
sd_netlink_call_async_cancel(nl, c->serial);
if (r < 0) {
(void) hashmap_remove(nl->reply_callbacks, &c->serial);
return r;
}
}
@@ -552,6 +565,8 @@ int sd_netlink_call_async(sd_netlink *nl,
if (serial)
*serial = s;
TAKE_PTR(c);
return k;
}
@@ -570,6 +585,9 @@ int sd_netlink_call_async_cancel(sd_netlink *nl, uint32_t serial) {
if (c->timeout != 0)
prioq_remove(nl->reply_callbacks_prioq, c, &c->prioq_idx);
if (c->destroy_callback)
c->destroy_callback(c->userdata);
free(c);
return 1;
}
@@ -827,13 +845,15 @@ int sd_netlink_add_match(sd_netlink *rtnl,
assert_return(callback, -EINVAL);
assert_return(!rtnl_pid_changed(rtnl), -ECHILD);
c = new0(struct match_callback, 1);
c = new(struct match_callback, 1);
if (!c)
return -ENOMEM;
c->callback = callback;
c->type = type;
c->userdata = userdata;
*c = (struct match_callback) {
.callback = callback,
.type = type,
.userdata = userdata,
};
switch (type) {
case RTM_NEWLINK:
+123 -32
View File
@@ -5,6 +5,7 @@
#include "sd-netlink.h"
#include "alloc-util.h"
#include "ether-addr-util.h"
#include "macro.h"
#include "missing.h"
@@ -177,8 +178,9 @@ static int link_handler(sd_netlink *rtnl, sd_netlink_message *m, void *userdata)
assert_se(rtnl);
assert_se(m);
assert_se(userdata);
log_info("got link info about %s", ifname);
log_info("%s: got link info about %s", __func__, ifname);
free(ifname);
assert_se(sd_netlink_message_read_string(m, IFLA_IFNAME, &data) >= 0);
@@ -199,7 +201,7 @@ static void test_event_loop(int ifindex) {
assert_se(sd_netlink_open(&rtnl) >= 0);
assert_se(sd_rtnl_message_new_link(rtnl, &m, RTM_GETLINK, ifindex) >= 0);
assert_se(sd_netlink_call_async(rtnl, m, link_handler, ifname, 0, NULL) >= 0);
assert_se(sd_netlink_call_async(rtnl, m, link_handler, NULL, ifname, 0, NULL) >= 0);
assert_se(sd_event_default(&event) >= 0);
@@ -212,6 +214,122 @@ static void test_event_loop(int ifindex) {
assert_se((rtnl = sd_netlink_unref(rtnl)) == NULL);
}
static void test_async(int ifindex) {
_cleanup_(sd_netlink_unrefp) sd_netlink *rtnl = NULL;
_cleanup_(sd_netlink_message_unrefp) sd_netlink_message *m = NULL, *r = NULL;
uint32_t serial;
char *ifname;
ifname = strdup("lo");
assert_se(ifname);
assert_se(sd_netlink_open(&rtnl) >= 0);
assert_se(sd_rtnl_message_new_link(rtnl, &m, RTM_GETLINK, ifindex) >= 0);
assert_se(sd_netlink_call_async(rtnl, m, link_handler, NULL, ifname, 0, &serial) >= 0);
assert_se(sd_netlink_wait(rtnl, 0) >= 0);
assert_se(sd_netlink_process(rtnl, &r) >= 0);
assert_se((rtnl = sd_netlink_unref(rtnl)) == NULL);
}
struct test_async_object {
unsigned n_ref;
char *ifname;
};
static struct test_async_object *test_async_object_free(struct test_async_object *t) {
assert(t);
free(t->ifname);
return mfree(t);
}
DEFINE_PRIVATE_TRIVIAL_REF_UNREF_FUNC(struct test_async_object, test_async_object, test_async_object_free);
DEFINE_TRIVIAL_CLEANUP_FUNC(struct test_async_object *, test_async_object_unref);
static int link_handler2(sd_netlink *rtnl, sd_netlink_message *m, void *userdata) {
struct test_async_object *t = userdata;
const char *data;
assert_se(rtnl);
assert_se(m);
assert_se(userdata);
log_info("%s: got link info about %s", __func__, t->ifname);
assert_se(sd_netlink_message_read_string(m, IFLA_IFNAME, &data) >= 0);
assert_se(streq(data, "lo"));
return 1;
}
static void test_async_object_destroy(void *userdata) {
struct test_async_object *t = userdata;
assert(userdata);
log_info("%s: n_ref=%u", __func__, t->n_ref);
test_async_object_unref(t);
}
static void test_async_destroy_callback(int ifindex) {
_cleanup_(sd_netlink_unrefp) sd_netlink *rtnl = NULL;
_cleanup_(sd_netlink_message_unrefp) sd_netlink_message *m = NULL, *r = NULL;
_cleanup_(test_async_object_unrefp) struct test_async_object *t = NULL;
uint32_t serial;
char *ifname;
assert_se(t = new(struct test_async_object, 1));
assert_se(ifname = strdup("lo"));
*t = (struct test_async_object) {
.n_ref = 1,
.ifname = ifname,
};
assert_se(sd_netlink_open(&rtnl) >= 0);
/* destroy callback is called after processing message */
assert_se(sd_rtnl_message_new_link(rtnl, &m, RTM_GETLINK, ifindex) >= 0);
assert_se(sd_netlink_call_async(rtnl, m, link_handler2, test_async_object_destroy, t, 0, &serial) >= 0);
assert_se(t->n_ref == 1);
assert_se(test_async_object_ref(t));
assert_se(t->n_ref == 2);
assert_se(sd_netlink_wait(rtnl, 0) >= 0);
assert_se(sd_netlink_process(rtnl, &r) == 1);
assert_se(t->n_ref == 1);
assert_se(!sd_netlink_message_unref(m));
/* destroy callback is called when asynchronous call is cancelled */
assert_se(sd_rtnl_message_new_link(rtnl, &m, RTM_GETLINK, ifindex) >= 0);
assert_se(sd_netlink_call_async(rtnl, m, link_handler2, test_async_object_destroy, t, 0, &serial) >= 0);
assert_se(t->n_ref == 1);
assert_se(test_async_object_ref(t));
assert_se(t->n_ref == 2);
assert_se(sd_netlink_call_async_cancel(rtnl, serial) >= 0);
assert_se(t->n_ref == 1);
assert_se(!sd_netlink_message_unref(m));
/* destroy callback is also called by sd_netlink_unref() */
assert_se(sd_rtnl_message_new_link(rtnl, &m, RTM_GETLINK, ifindex) >= 0);
assert_se(sd_netlink_call_async(rtnl, m, link_handler2, test_async_object_destroy, t, 0, &serial) >= 0);
assert_se(t->n_ref == 1);
assert_se(test_async_object_ref(t));
assert_se(t->n_ref == 2);
assert_se((rtnl = sd_netlink_unref(rtnl)) == NULL);
assert_se(t->n_ref == 1);
}
static int pipe_handler(sd_netlink *rtnl, sd_netlink_message *m, void *userdata) {
int *counter = userdata;
int r;
@@ -227,27 +345,6 @@ static int pipe_handler(sd_netlink *rtnl, sd_netlink_message *m, void *userdata)
return 1;
}
static void test_async(int ifindex) {
_cleanup_(sd_netlink_unrefp) sd_netlink *rtnl = NULL;
_cleanup_(sd_netlink_message_unrefp) sd_netlink_message *m = NULL, *r = NULL;
uint32_t serial;
char *ifname;
ifname = strdup("lo");
assert_se(ifname);
assert_se(sd_netlink_open(&rtnl) >= 0);
assert_se(sd_rtnl_message_new_link(rtnl, &m, RTM_GETLINK, ifindex) >= 0);
assert_se(sd_netlink_call_async(rtnl, m, link_handler, ifname, 0, &serial) >= 0);
assert_se(sd_netlink_wait(rtnl, 0) >= 0);
assert_se(sd_netlink_process(rtnl, &r) >= 0);
assert_se((rtnl = sd_netlink_unref(rtnl)) == NULL);
}
static void test_pipe(int ifindex) {
_cleanup_(sd_netlink_unrefp) sd_netlink *rtnl = NULL;
_cleanup_(sd_netlink_message_unrefp) sd_netlink_message *m1 = NULL, *m2 = NULL;
@@ -259,10 +356,10 @@ static void test_pipe(int ifindex) {
assert_se(sd_rtnl_message_new_link(rtnl, &m2, RTM_GETLINK, ifindex) >= 0);
counter++;
assert_se(sd_netlink_call_async(rtnl, m1, pipe_handler, &counter, 0, NULL) >= 0);
assert_se(sd_netlink_call_async(rtnl, m1, pipe_handler, NULL, &counter, 0, NULL) >= 0);
counter++;
assert_se(sd_netlink_call_async(rtnl, m2, pipe_handler, &counter, 0, NULL) >= 0);
assert_se(sd_netlink_call_async(rtnl, m2, pipe_handler, NULL, &counter, 0, NULL) >= 0);
while (counter > 0) {
assert_se(sd_netlink_wait(rtnl, 0) >= 0);
@@ -366,31 +463,25 @@ int main(void) {
uint16_t type;
test_match();
test_multiple();
assert_se(sd_netlink_open(&rtnl) >= 0);
assert_se(rtnl);
test_route(rtnl);
test_message(rtnl);
test_container(rtnl);
if_loopback = (int) if_nametoindex("lo");
assert_se(if_loopback > 0);
test_async(if_loopback);
test_async_destroy_callback(if_loopback);
test_pipe(if_loopback);
test_event_loop(if_loopback);
test_link_configure(rtnl, if_loopback);
test_get_addresses(rtnl);
test_message_link_bridge(rtnl);
assert_se(sd_rtnl_message_new_link(rtnl, &m, RTM_GETLINK, if_loopback) >= 0);