| /* SPDX-License-Identifier: LGPL-2.1-or-later */ |
| |
| #include <errno.h> |
| |
| #include "sd-netlink.h" |
| |
| #include "alloc-util.h" |
| #include "netlink-internal.h" |
| #include "netlink-slot.h" |
| #include "string-util.h" |
| |
| int netlink_slot_allocate( |
| sd_netlink *nl, |
| bool floating, |
| NetlinkSlotType type, |
| size_t extra, |
| void *userdata, |
| const char *description, |
| sd_netlink_slot **ret) { |
| |
| _cleanup_free_ sd_netlink_slot *slot = NULL; |
| |
| assert(nl); |
| assert(ret); |
| |
| slot = malloc0(offsetof(sd_netlink_slot, reply_callback) + extra); |
| if (!slot) |
| return -ENOMEM; |
| |
| slot->n_ref = 1; |
| slot->netlink = nl; |
| slot->userdata = userdata; |
| slot->type = type; |
| slot->floating = floating; |
| |
| if (description) { |
| slot->description = strdup(description); |
| if (!slot->description) |
| return -ENOMEM; |
| } |
| |
| if (!floating) |
| sd_netlink_ref(nl); |
| |
| LIST_PREPEND(slots, nl->slots, slot); |
| |
| *ret = TAKE_PTR(slot); |
| |
| return 0; |
| } |
| |
| void netlink_slot_disconnect(sd_netlink_slot *slot, bool unref) { |
| sd_netlink *nl; |
| |
| assert(slot); |
| |
| nl = slot->netlink; |
| if (!nl) |
| return; |
| |
| switch (slot->type) { |
| |
| case NETLINK_REPLY_CALLBACK: |
| (void) hashmap_remove(nl->reply_callbacks, &slot->reply_callback.serial); |
| |
| if (slot->reply_callback.timeout != 0) |
| prioq_remove(nl->reply_callbacks_prioq, &slot->reply_callback, &slot->reply_callback.prioq_idx); |
| |
| break; |
| case NETLINK_MATCH_CALLBACK: |
| LIST_REMOVE(match_callbacks, nl->match_callbacks, &slot->match_callback); |
| |
| switch (slot->match_callback.type) { |
| case RTM_NEWLINK: |
| case RTM_DELLINK: |
| (void) socket_broadcast_group_unref(nl, RTNLGRP_LINK); |
| |
| break; |
| case RTM_NEWADDR: |
| case RTM_DELADDR: |
| (void) socket_broadcast_group_unref(nl, RTNLGRP_IPV4_IFADDR); |
| (void) socket_broadcast_group_unref(nl, RTNLGRP_IPV6_IFADDR); |
| |
| break; |
| case RTM_NEWROUTE: |
| case RTM_DELROUTE: |
| (void) socket_broadcast_group_unref(nl, RTNLGRP_IPV4_ROUTE); |
| (void) socket_broadcast_group_unref(nl, RTNLGRP_IPV6_ROUTE); |
| |
| break; |
| } |
| |
| break; |
| default: |
| assert_not_reached("Wut? Unknown slot type?"); |
| } |
| |
| slot->type = _NETLINK_SLOT_INVALID; |
| slot->netlink = NULL; |
| LIST_REMOVE(slots, nl->slots, slot); |
| |
| if (!slot->floating) |
| sd_netlink_unref(nl); |
| else if (unref) |
| sd_netlink_slot_unref(slot); |
| } |
| |
| static sd_netlink_slot* netlink_slot_free(sd_netlink_slot *slot) { |
| assert(slot); |
| |
| netlink_slot_disconnect(slot, false); |
| |
| if (slot->destroy_callback) |
| slot->destroy_callback(slot->userdata); |
| |
| free(slot->description); |
| return mfree(slot); |
| } |
| |
| DEFINE_PUBLIC_TRIVIAL_REF_UNREF_FUNC(sd_netlink_slot, sd_netlink_slot, netlink_slot_free); |
| |
| sd_netlink *sd_netlink_slot_get_netlink(sd_netlink_slot *slot) { |
| assert_return(slot, NULL); |
| |
| return slot->netlink; |
| } |
| |
| void *sd_netlink_slot_get_userdata(sd_netlink_slot *slot) { |
| assert_return(slot, NULL); |
| |
| return slot->userdata; |
| } |
| |
| void *sd_netlink_slot_set_userdata(sd_netlink_slot *slot, void *userdata) { |
| void *ret; |
| |
| assert_return(slot, NULL); |
| |
| ret = slot->userdata; |
| slot->userdata = userdata; |
| |
| return ret; |
| } |
| |
| int sd_netlink_slot_get_destroy_callback(const sd_netlink_slot *slot, sd_netlink_destroy_t *callback) { |
| assert_return(slot, -EINVAL); |
| |
| if (callback) |
| *callback = slot->destroy_callback; |
| |
| return !!slot->destroy_callback; |
| } |
| |
| int sd_netlink_slot_set_destroy_callback(sd_netlink_slot *slot, sd_netlink_destroy_t callback) { |
| assert_return(slot, -EINVAL); |
| |
| slot->destroy_callback = callback; |
| return 0; |
| } |
| |
| int sd_netlink_slot_get_floating(const sd_netlink_slot *slot) { |
| assert_return(slot, -EINVAL); |
| |
| return slot->floating; |
| } |
| |
| int sd_netlink_slot_set_floating(sd_netlink_slot *slot, int b) { |
| assert_return(slot, -EINVAL); |
| |
| if (slot->floating == !!b) |
| return 0; |
| |
| if (!slot->netlink) /* Already disconnected */ |
| return -ESTALE; |
| |
| slot->floating = b; |
| |
| if (b) { |
| sd_netlink_slot_ref(slot); |
| sd_netlink_unref(slot->netlink); |
| } else { |
| sd_netlink_ref(slot->netlink); |
| sd_netlink_slot_unref(slot); |
| } |
| |
| return 1; |
| } |
| |
| int sd_netlink_slot_get_description(const sd_netlink_slot *slot, const char **description) { |
| assert_return(slot, -EINVAL); |
| |
| if (description) |
| *description = slot->description; |
| |
| return !!slot->description; |
| } |
| |
| int sd_netlink_slot_set_description(sd_netlink_slot *slot, const char *description) { |
| assert_return(slot, -EINVAL); |
| |
| return free_and_strdup(&slot->description, description); |
| } |