diff options
Diffstat (limited to 'src/libsystemd/sd-netlink/sd-netlink.c')
-rw-r--r-- | src/libsystemd/sd-netlink/sd-netlink.c | 117 |
1 files changed, 79 insertions, 38 deletions
diff --git a/src/libsystemd/sd-netlink/sd-netlink.c b/src/libsystemd/sd-netlink/sd-netlink.c index c413b1c266..43114eb825 100644 --- a/src/libsystemd/sd-netlink/sd-netlink.c +++ b/src/libsystemd/sd-netlink/sd-netlink.c @@ -1,5 +1,3 @@ -/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/ - /*** This file is part of systemd. @@ -19,20 +17,23 @@ along with systemd; If not, see <http://www.gnu.org/licenses/>. ***/ -#include <sys/socket.h> #include <poll.h> - -#include "missing.h" -#include "macro.h" -#include "util.h" -#include "hashmap.h" +#include <sys/socket.h> #include "sd-netlink.h" + +#include "alloc-util.h" +#include "fd-util.h" +#include "hashmap.h" +#include "macro.h" +#include "missing.h" #include "netlink-internal.h" #include "netlink-util.h" +#include "socket-util.h" +#include "util.h" static int sd_netlink_new(sd_netlink **ret) { - _cleanup_netlink_unref_ sd_netlink *rtnl = NULL; + _cleanup_(sd_netlink_unrefp) sd_netlink *rtnl = NULL; assert_return(ret, -EINVAL); @@ -41,11 +42,8 @@ static int sd_netlink_new(sd_netlink **ret) { return -ENOMEM; rtnl->n_ref = REFCNT_INIT; - rtnl->fd = -1; - rtnl->sockaddr.nl.nl_family = AF_NETLINK; - rtnl->original_pid = getpid(); LIST_HEAD_INIT(rtnl->match_callbacks); @@ -68,7 +66,7 @@ static int sd_netlink_new(sd_netlink **ret) { } int sd_netlink_new_from_netlink(sd_netlink **ret, int fd) { - _cleanup_netlink_unref_ sd_netlink *rtnl = NULL; + _cleanup_(sd_netlink_unrefp) sd_netlink *rtnl = NULL; socklen_t addrlen; int r; @@ -84,6 +82,9 @@ int sd_netlink_new_from_netlink(sd_netlink **ret, int fd) { if (r < 0) return -errno; + if (rtnl->sockaddr.nl.nl_family != AF_NETLINK) + return -EINVAL; + rtnl->fd = fd; *ret = rtnl; @@ -102,11 +103,11 @@ static bool rtnl_pid_changed(sd_netlink *rtnl) { } int sd_netlink_open_fd(sd_netlink **ret, int fd) { - _cleanup_netlink_unref_ sd_netlink *rtnl = NULL; + _cleanup_(sd_netlink_unrefp) sd_netlink *rtnl = NULL; int r; assert_return(ret, -EINVAL); - assert_return(fd >= 0, -EINVAL); + assert_return(fd >= 0, -EBADF); r = sd_netlink_new(&rtnl); if (r < 0) @@ -115,8 +116,10 @@ int sd_netlink_open_fd(sd_netlink **ret, int fd) { rtnl->fd = fd; r = socket_bind(rtnl); - if (r < 0) + if (r < 0) { + rtnl->fd = -1; /* on failure, the caller remains owner of the fd, hence don't close it here */ return r; + } *ret = rtnl; rtnl = NULL; @@ -141,7 +144,10 @@ int sd_netlink_open(sd_netlink **ret) { return 0; } -int sd_netlink_inc_rcvbuf(const sd_netlink *const rtnl, const int size) { +int sd_netlink_inc_rcvbuf(sd_netlink *rtnl, size_t size) { + assert_return(rtnl, -EINVAL); + assert_return(!rtnl_pid_changed(rtnl), -ECHILD); + return fd_inc_rcvbuf(rtnl->fd, size); } @@ -183,10 +189,11 @@ sd_netlink *sd_netlink_unref(sd_netlink *rtnl) { sd_event_unref(rtnl->event); while ((f = rtnl->match_callbacks)) { - LIST_REMOVE(match_callbacks, rtnl->match_callbacks, f); - free(f); + sd_netlink_remove_match(rtnl, f->type, f->callback, f->userdata); } + hashmap_free(rtnl->broadcast_group_refs); + safe_close(rtnl->fd); free(rtnl); } @@ -275,14 +282,14 @@ static int dispatch_rqueue(sd_netlink *rtnl, sd_netlink_message **message) { /* Dispatch a queued message */ *message = rtnl->rqueue[0]; - rtnl->rqueue_size --; + rtnl->rqueue_size--; memmove(rtnl->rqueue, rtnl->rqueue + 1, sizeof(sd_netlink_message*) * rtnl->rqueue_size); return 1; } static int process_timeout(sd_netlink *rtnl) { - _cleanup_netlink_message_unref_ sd_netlink_message *m = NULL; + _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *m = NULL; struct reply_callback *c; usec_t n; int r; @@ -372,7 +379,7 @@ static int process_match(sd_netlink *rtnl, sd_netlink_message *m) { } static int process_running(sd_netlink *rtnl, sd_netlink_message **ret) { - _cleanup_netlink_message_unref_ sd_netlink_message *m = NULL; + _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *m = NULL; int r; assert(rtnl); @@ -414,7 +421,7 @@ null_message: } int sd_netlink_process(sd_netlink *rtnl, sd_netlink_message **ret) { - RTNL_DONT_DESTROY(rtnl); + NETLINK_DONT_DESTROY(rtnl); int r; assert_return(rtnl, -EINVAL); @@ -619,7 +626,7 @@ int sd_netlink_call(sd_netlink *rtnl, received_serial = rtnl_message_get_serial(rtnl->rqueue[i]); if (received_serial == serial) { - _cleanup_netlink_message_unref_ sd_netlink_message *incoming = NULL; + _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *incoming = NULL; uint16_t type; incoming = rtnl->rqueue[i]; @@ -770,7 +777,7 @@ static int prepare_callback(sd_event_source *s, void *userdata) { return 1; } -int sd_netlink_attach_event(sd_netlink *rtnl, sd_event *event, int priority) { +int sd_netlink_attach_event(sd_netlink *rtnl, sd_event *event, int64_t priority) { int r; assert_return(rtnl, -EINVAL); @@ -856,25 +863,32 @@ int sd_netlink_add_match(sd_netlink *rtnl, switch (type) { case RTM_NEWLINK: - case RTM_SETLINK: - case RTM_GETLINK: case RTM_DELLINK: - r = socket_join_broadcast_group(rtnl, RTNLGRP_LINK); + r = socket_broadcast_group_ref(rtnl, RTNLGRP_LINK); if (r < 0) return r; break; case RTM_NEWADDR: - case RTM_GETADDR: case RTM_DELADDR: - r = socket_join_broadcast_group(rtnl, RTNLGRP_IPV4_IFADDR); + r = socket_broadcast_group_ref(rtnl, RTNLGRP_IPV4_IFADDR); + if (r < 0) + return r; + + r = socket_broadcast_group_ref(rtnl, RTNLGRP_IPV6_IFADDR); if (r < 0) return r; - r = socket_join_broadcast_group(rtnl, RTNLGRP_IPV6_IFADDR); + break; + case RTM_NEWROUTE: + case RTM_DELROUTE: + r = socket_broadcast_group_ref(rtnl, RTNLGRP_IPV4_ROUTE); if (r < 0) return r; + r = socket_broadcast_group_ref(rtnl, RTNLGRP_IPV6_ROUTE); + if (r < 0) + return r; break; default: return -EOPNOTSUPP; @@ -892,23 +906,50 @@ int sd_netlink_remove_match(sd_netlink *rtnl, sd_netlink_message_handler_t callback, void *userdata) { struct match_callback *c; + int r; assert_return(rtnl, -EINVAL); assert_return(callback, -EINVAL); assert_return(!rtnl_pid_changed(rtnl), -ECHILD); - /* we should unsubscribe from the broadcast groups at this point, but it is not so - trivial for a few reasons: the refcounting is a bit of a mess and not obvious - how it will look like after we add genetlink support, and it is also not possible - to query what broadcast groups were subscribed to when we inherit the socket to get - the initial refcount. The latter could indeed be done for the first 32 broadcast - groups (which incidentally is all we currently support in .socket units anyway), - but we better not rely on only ever using 32 groups. */ LIST_FOREACH(match_callbacks, c, rtnl->match_callbacks) if (c->callback == callback && c->type == type && c->userdata == userdata) { LIST_REMOVE(match_callbacks, rtnl->match_callbacks, c); free(c); + switch (type) { + case RTM_NEWLINK: + case RTM_DELLINK: + r = socket_broadcast_group_unref(rtnl, RTNLGRP_LINK); + if (r < 0) + return r; + + break; + case RTM_NEWADDR: + case RTM_DELADDR: + r = socket_broadcast_group_unref(rtnl, RTNLGRP_IPV4_IFADDR); + if (r < 0) + return r; + + r = socket_broadcast_group_unref(rtnl, RTNLGRP_IPV6_IFADDR); + if (r < 0) + return r; + + break; + case RTM_NEWROUTE: + case RTM_DELROUTE: + r = socket_broadcast_group_unref(rtnl, RTNLGRP_IPV4_ROUTE); + if (r < 0) + return r; + + r = socket_broadcast_group_unref(rtnl, RTNLGRP_IPV6_ROUTE); + if (r < 0) + return r; + break; + default: + return -EOPNOTSUPP; + } + return 1; } |