203 lines
5.3 KiB
C
203 lines
5.3 KiB
C
/* 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);
|
|
}
|