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:
parent
a02113d2ea
commit
8cec01b9e9
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
Loading…
Reference in a new issue