summaryrefslogtreecommitdiff
path: root/src/libsystemd-rtnl
diff options
context:
space:
mode:
authorTom Gundersen <teg@jklm.no>2013-11-30 01:24:29 +0100
committerTom Gundersen <teg@jklm.no>2013-12-04 14:15:13 +0100
commit8cec01b9e9b7264639fd1fc0fe373ef4d5baaf49 (patch)
tree6db638964058da2a30a7839fc7fe03de9b3c3732 /src/libsystemd-rtnl
parenta02113d2ea96467dc9f1ec196d5f62a34dd99314 (diff)
rtnl: add callback support
sd_rtnl_add_match allows you to add a callback function for when given types of messages are received.
Diffstat (limited to 'src/libsystemd-rtnl')
-rw-r--r--src/libsystemd-rtnl/rtnl-internal.h11
-rw-r--r--src/libsystemd-rtnl/sd-rtnl.c80
-rw-r--r--src/libsystemd-rtnl/test-rtnl.c17
3 files changed, 108 insertions, 0 deletions
diff --git a/src/libsystemd-rtnl/rtnl-internal.h b/src/libsystemd-rtnl/rtnl-internal.h
index a229ae77ad..dabf12d37f 100644
--- a/src/libsystemd-rtnl/rtnl-internal.h
+++ b/src/libsystemd-rtnl/rtnl-internal.h
@@ -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;
diff --git a/src/libsystemd-rtnl/sd-rtnl.c b/src/libsystemd-rtnl/sd-rtnl.c
index 8631ff05e0..57d0b766f2 100644
--- a/src/libsystemd-rtnl/sd-rtnl.c
+++ b/src/libsystemd-rtnl/sd-rtnl.c
@@ -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;
+}
diff --git a/src/libsystemd-rtnl/test-rtnl.c b/src/libsystemd-rtnl/test-rtnl.c
index 4b62c02361..a512a7b2f7 100644
--- a/src/libsystemd-rtnl/test-rtnl.c
+++ b/src/libsystemd-rtnl/test-rtnl.c
@@ -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();