diff options
Diffstat (limited to 'src/libsystemd-rtnl')
-rw-r--r-- | src/libsystemd-rtnl/rtnl-internal.h | 11 | ||||
-rw-r--r-- | src/libsystemd-rtnl/sd-rtnl.c | 80 | ||||
-rw-r--r-- | src/libsystemd-rtnl/test-rtnl.c | 17 |
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(); |