rtnl: add callback support

sd_rtnl_add_match allows you to add a callback function for when given types of
messages are received.
This commit is contained in:
Tom Gundersen 2013-11-30 01:24:29 +01:00
parent a02113d2ea
commit 8cec01b9e9
4 changed files with 111 additions and 0 deletions

View file

@ -25,6 +25,7 @@
#include "refcnt.h"
#include "prioq.h"
#include "list.h"
#include "sd-rtnl.h"
@ -36,6 +37,14 @@ struct reply_callback {
unsigned prioq_idx;
};
struct match_callback {
sd_rtnl_message_handler_t callback;
uint16_t types;
void *userdata;
LIST_FIELDS(struct match_callback, match_callbacks);
};
struct sd_rtnl {
RefCount n_ref;
@ -59,6 +68,8 @@ struct sd_rtnl {
struct Prioq *reply_callbacks_prioq;
Hashmap *reply_callbacks;
LIST_HEAD(struct match_callback, match_callbacks);
pid_t original_pid;
sd_event_source *io_event_source;

View file

@ -47,6 +47,8 @@ static int sd_rtnl_new(sd_rtnl **ret) {
rtnl->original_pid = getpid();
LIST_HEAD_INIT(rtnl->match_callbacks);
/* We guarantee that wqueue always has space for at least
* one entry */
rtnl->wqueue = new(sd_rtnl_message*, 1);
@ -109,6 +111,7 @@ sd_rtnl *sd_rtnl_ref(sd_rtnl *rtnl) {
sd_rtnl *sd_rtnl_unref(sd_rtnl *rtnl) {
if (rtnl && REFCNT_DEC(rtnl->n_ref) <= 0) {
struct match_callback *f;
unsigned i;
for (i = 0; i < rtnl->rqueue_size; i++)
@ -122,6 +125,11 @@ sd_rtnl *sd_rtnl_unref(sd_rtnl *rtnl) {
hashmap_free_free(rtnl->reply_callbacks);
prioq_free(rtnl->reply_callbacks_prioq);
while ((f = rtnl->match_callbacks)) {
LIST_REMOVE(match_callbacks, rtnl->match_callbacks, f);
free(f);
}
if (rtnl->fd >= 0)
close_nointr_nofail(rtnl->fd);
@ -281,6 +289,29 @@ static int process_reply(sd_rtnl *rtnl, sd_rtnl_message *m) {
return r;
}
static int process_match(sd_rtnl *rtnl, sd_rtnl_message *m) {
struct match_callback *c;
uint16_t type;
int r;
assert(rtnl);
assert(m);
r = sd_rtnl_message_get_type(m, &type);
if (r < 0)
return r;
LIST_FOREACH(match_callbacks, c, rtnl->match_callbacks) {
if (type & c->types) {
r = c->callback(rtnl, m, c->userdata);
if (r != 0)
return r;
}
}
return 0;
}
static int process_running(sd_rtnl *rtnl, sd_rtnl_message **ret) {
_cleanup_sd_rtnl_message_unref_ sd_rtnl_message *m = NULL;
int r;
@ -303,6 +334,10 @@ static int process_running(sd_rtnl *rtnl, sd_rtnl_message **ret) {
if (r != 0)
goto null_message;
r = process_match(rtnl, m);
if (r != 0)
goto null_message;
if (ret) {
*ret = m;
m = NULL;
@ -784,3 +819,48 @@ int sd_rtnl_detach_event(sd_rtnl *rtnl) {
return 0;
}
int sd_rtnl_add_match(sd_rtnl *rtnl,
uint16_t types,
sd_rtnl_message_handler_t callback,
void *userdata) {
struct match_callback *c;
assert_return(rtnl, -EINVAL);
assert_return(callback, -EINVAL);
assert_return(types, -EINVAL);
assert_return(!rtnl_pid_changed(rtnl), -ECHILD);
c = new0(struct match_callback, 1);
if (!c)
return -ENOMEM;
c->callback = callback;
c->types = types;
c->userdata = userdata;
LIST_PREPEND(match_callbacks, rtnl->match_callbacks, c);
return 0;
}
int sd_rtnl_remove_match(sd_rtnl *rtnl,
uint16_t types,
sd_rtnl_message_handler_t callback,
void *userdata) {
struct match_callback *c;
assert_return(rtnl, -EINVAL);
assert_return(callback, -EINVAL);
assert_return(!rtnl_pid_changed(rtnl), -ECHILD);
LIST_FOREACH(match_callbacks, c, rtnl->match_callbacks)
if (c->callback == callback && c->types == types && c->userdata == userdata) {
LIST_REMOVE(match_callbacks, rtnl->match_callbacks, c);
free(c);
return 1;
}
return 0;
}

View file

@ -230,6 +230,21 @@ static void test_container(void) {
*/
}
static void test_match(void) {
_cleanup_sd_rtnl_unref_ sd_rtnl *rtnl = NULL;
assert(sd_rtnl_open(0, &rtnl) >= 0);
assert(sd_rtnl_add_match(rtnl, 0, &link_handler, NULL) == -EINVAL);
assert(sd_rtnl_add_match(rtnl, RTMGRP_LINK, &link_handler, NULL) >= 0);
assert(sd_rtnl_add_match(rtnl, RTMGRP_LINK, &link_handler, NULL) >= 0);
assert(sd_rtnl_remove_match(rtnl, RTMGRP_LINK, &link_handler, NULL) == 1);
assert(sd_rtnl_remove_match(rtnl, RTMGRP_LINK, &link_handler, NULL) == 1);
assert(sd_rtnl_remove_match(rtnl, RTMGRP_LINK, &link_handler, NULL) == 0);
}
int main(void) {
sd_rtnl *rtnl;
sd_rtnl_message *m;
@ -240,6 +255,8 @@ int main(void) {
unsigned int mtu = 0;
unsigned int *mtu_reply;
test_match();
test_multiple();
test_route();

View file

@ -57,6 +57,9 @@ int sd_rtnl_process(sd_rtnl *nl, sd_rtnl_message **ret);
int sd_rtnl_wait(sd_rtnl *nl, uint64_t timeout);
int sd_rtnl_flush(sd_rtnl *nl);
int sd_rtnl_add_match(sd_rtnl *nl, uint16_t match, sd_rtnl_message_handler_t c, void *userdata);
int sd_rtnl_remove_match(sd_rtnl *nl, uint16_t match, sd_rtnl_message_handler_t c, void *userdata);
int sd_rtnl_attach_event(sd_rtnl *nl, sd_event *e, int priority);
int sd_rtnl_detach_event(sd_rtnl *nl);