2017-11-18 17:09:20 +01:00
|
|
|
/* SPDX-License-Identifier: LGPL-2.1+ */
|
2013-10-21 20:12:52 +02:00
|
|
|
|
|
|
|
#include <poll.h>
|
|
|
|
|
2015-06-12 16:31:33 +02:00
|
|
|
#include "sd-netlink.h"
|
2015-10-24 22:58:24 +02:00
|
|
|
|
2015-10-27 03:01:06 +01:00
|
|
|
#include "alloc-util.h"
|
2015-10-25 13:14:12 +01:00
|
|
|
#include "fd-util.h"
|
2015-10-24 22:58:24 +02:00
|
|
|
#include "hashmap.h"
|
2020-06-10 11:43:40 +02:00
|
|
|
#include "io-util.h"
|
2015-10-24 22:58:24 +02:00
|
|
|
#include "macro.h"
|
2015-06-12 16:31:33 +02:00
|
|
|
#include "netlink-internal.h"
|
2018-10-15 09:06:12 +02:00
|
|
|
#include "netlink-slot.h"
|
2015-06-12 16:31:33 +02:00
|
|
|
#include "netlink-util.h"
|
2018-01-11 00:39:12 +01:00
|
|
|
#include "process-util.h"
|
2015-10-26 01:09:02 +01:00
|
|
|
#include "socket-util.h"
|
2018-10-15 10:49:53 +02:00
|
|
|
#include "string-util.h"
|
2015-10-24 22:58:24 +02:00
|
|
|
#include "util.h"
|
2013-10-21 20:12:52 +02:00
|
|
|
|
2015-06-12 16:31:33 +02:00
|
|
|
static int sd_netlink_new(sd_netlink **ret) {
|
tree-wide: expose "p"-suffix unref calls in public APIs to make gcc cleanup easy
GLIB has recently started to officially support the gcc cleanup
attribute in its public API, hence let's do the same for our APIs.
With this patch we'll define an xyz_unrefp() call for each public
xyz_unref() call, to make it easy to use inside a
__attribute__((cleanup())) expression. Then, all code is ported over to
make use of this.
The new calls are also documented in the man pages, with examples how to
use them (well, I only added docs where the _unref() call itself already
had docs, and the examples, only cover sd_bus_unrefp() and
sd_event_unrefp()).
This also renames sd_lldp_free() to sd_lldp_unref(), since that's how we
tend to call our destructors these days.
Note that this defines no public macro that wraps gcc's attribute and
makes it easier to use. While I think it's our duty in the library to
make our stuff easy to use, I figure it's not our duty to make gcc's own
features easy to use on its own. Most likely, client code which wants to
make use of this should define its own:
#define _cleanup_(function) __attribute__((cleanup(function)))
Or similar, to make the gcc feature easier to use.
Making this logic public has the benefit that we can remove three header
files whose only purpose was to define these functions internally.
See #2008.
2015-11-27 19:13:45 +01:00
|
|
|
_cleanup_(sd_netlink_unrefp) sd_netlink *rtnl = NULL;
|
2013-10-21 20:12:52 +02:00
|
|
|
|
|
|
|
assert_return(ret, -EINVAL);
|
|
|
|
|
2018-10-03 20:21:52 +02:00
|
|
|
rtnl = new(sd_netlink, 1);
|
2013-10-21 20:12:52 +02:00
|
|
|
if (!rtnl)
|
|
|
|
return -ENOMEM;
|
|
|
|
|
2018-10-03 20:21:52 +02:00
|
|
|
*rtnl = (sd_netlink) {
|
2019-03-01 18:31:45 +01:00
|
|
|
.n_ref = 1,
|
2018-10-03 20:21:52 +02:00
|
|
|
.fd = -1,
|
|
|
|
.sockaddr.nl.nl_family = AF_NETLINK,
|
|
|
|
.original_pid = getpid_cached(),
|
|
|
|
.protocol = -1,
|
2013-10-29 17:38:31 +01:00
|
|
|
|
2018-10-03 20:21:52 +02:00
|
|
|
/* Change notification responses have sequence 0, so we must
|
|
|
|
* start our request sequence numbers at 1, or we may confuse our
|
|
|
|
* responses with notifications from the kernel */
|
|
|
|
.serial = 1,
|
|
|
|
|
|
|
|
};
|
2013-11-30 01:24:29 +01:00
|
|
|
|
2014-04-14 17:20:51 +02:00
|
|
|
/* We guarantee that the read buffer has at least space for
|
|
|
|
* a message header */
|
|
|
|
if (!greedy_realloc((void**)&rtnl->rbuffer, &rtnl->rbuffer_allocated,
|
|
|
|
sizeof(struct nlmsghdr), sizeof(uint8_t)))
|
|
|
|
return -ENOMEM;
|
|
|
|
|
2018-04-05 07:26:26 +02:00
|
|
|
*ret = TAKE_PTR(rtnl);
|
2014-03-31 14:43:34 +02:00
|
|
|
|
2013-10-21 20:12:52 +02:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2015-06-12 16:31:33 +02:00
|
|
|
int sd_netlink_new_from_netlink(sd_netlink **ret, int fd) {
|
tree-wide: expose "p"-suffix unref calls in public APIs to make gcc cleanup easy
GLIB has recently started to officially support the gcc cleanup
attribute in its public API, hence let's do the same for our APIs.
With this patch we'll define an xyz_unrefp() call for each public
xyz_unref() call, to make it easy to use inside a
__attribute__((cleanup())) expression. Then, all code is ported over to
make use of this.
The new calls are also documented in the man pages, with examples how to
use them (well, I only added docs where the _unref() call itself already
had docs, and the examples, only cover sd_bus_unrefp() and
sd_event_unrefp()).
This also renames sd_lldp_free() to sd_lldp_unref(), since that's how we
tend to call our destructors these days.
Note that this defines no public macro that wraps gcc's attribute and
makes it easier to use. While I think it's our duty in the library to
make our stuff easy to use, I figure it's not our duty to make gcc's own
features easy to use on its own. Most likely, client code which wants to
make use of this should define its own:
#define _cleanup_(function) __attribute__((cleanup(function)))
Or similar, to make the gcc feature easier to use.
Making this logic public has the benefit that we can remove three header
files whose only purpose was to define these functions internally.
See #2008.
2015-11-27 19:13:45 +01:00
|
|
|
_cleanup_(sd_netlink_unrefp) sd_netlink *rtnl = NULL;
|
2015-02-01 22:12:33 +01:00
|
|
|
socklen_t addrlen;
|
|
|
|
int r;
|
|
|
|
|
|
|
|
assert_return(ret, -EINVAL);
|
|
|
|
|
2015-06-12 16:31:33 +02:00
|
|
|
r = sd_netlink_new(&rtnl);
|
2015-02-01 22:12:33 +01:00
|
|
|
if (r < 0)
|
|
|
|
return r;
|
|
|
|
|
|
|
|
addrlen = sizeof(rtnl->sockaddr);
|
|
|
|
|
|
|
|
r = getsockname(fd, &rtnl->sockaddr.sa, &addrlen);
|
|
|
|
if (r < 0)
|
|
|
|
return -errno;
|
|
|
|
|
2016-02-01 22:17:35 +01:00
|
|
|
if (rtnl->sockaddr.nl.nl_family != AF_NETLINK)
|
|
|
|
return -EINVAL;
|
|
|
|
|
2015-02-01 22:12:33 +01:00
|
|
|
rtnl->fd = fd;
|
|
|
|
|
2018-04-05 07:26:26 +02:00
|
|
|
*ret = TAKE_PTR(rtnl);
|
2015-02-01 22:12:33 +01:00
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2019-10-24 11:40:44 +02:00
|
|
|
static bool rtnl_pid_changed(const sd_netlink *rtnl) {
|
2013-10-29 17:38:31 +01:00
|
|
|
assert(rtnl);
|
|
|
|
|
|
|
|
/* We don't support people creating an rtnl connection and
|
|
|
|
* keeping it around over a fork(). Let's complain. */
|
|
|
|
|
2017-07-20 16:19:18 +02:00
|
|
|
return rtnl->original_pid != getpid_cached();
|
2013-10-29 17:38:31 +01:00
|
|
|
}
|
|
|
|
|
2015-06-12 16:31:33 +02:00
|
|
|
int sd_netlink_open_fd(sd_netlink **ret, int fd) {
|
tree-wide: expose "p"-suffix unref calls in public APIs to make gcc cleanup easy
GLIB has recently started to officially support the gcc cleanup
attribute in its public API, hence let's do the same for our APIs.
With this patch we'll define an xyz_unrefp() call for each public
xyz_unref() call, to make it easy to use inside a
__attribute__((cleanup())) expression. Then, all code is ported over to
make use of this.
The new calls are also documented in the man pages, with examples how to
use them (well, I only added docs where the _unref() call itself already
had docs, and the examples, only cover sd_bus_unrefp() and
sd_event_unrefp()).
This also renames sd_lldp_free() to sd_lldp_unref(), since that's how we
tend to call our destructors these days.
Note that this defines no public macro that wraps gcc's attribute and
makes it easier to use. While I think it's our duty in the library to
make our stuff easy to use, I figure it's not our duty to make gcc's own
features easy to use on its own. Most likely, client code which wants to
make use of this should define its own:
#define _cleanup_(function) __attribute__((cleanup(function)))
Or similar, to make the gcc feature easier to use.
Making this logic public has the benefit that we can remove three header
files whose only purpose was to define these functions internally.
See #2008.
2015-11-27 19:13:45 +01:00
|
|
|
_cleanup_(sd_netlink_unrefp) sd_netlink *rtnl = NULL;
|
2015-06-13 21:25:05 +02:00
|
|
|
int r;
|
2017-12-18 15:17:06 +01:00
|
|
|
int protocol;
|
|
|
|
socklen_t l;
|
2013-10-21 20:12:52 +02:00
|
|
|
|
2013-12-06 15:13:34 +01:00
|
|
|
assert_return(ret, -EINVAL);
|
2015-08-14 12:45:06 +02:00
|
|
|
assert_return(fd >= 0, -EBADF);
|
2013-12-06 15:13:34 +01:00
|
|
|
|
2015-06-12 16:31:33 +02:00
|
|
|
r = sd_netlink_new(&rtnl);
|
2013-10-21 20:12:52 +02:00
|
|
|
if (r < 0)
|
|
|
|
return r;
|
|
|
|
|
2017-12-18 15:17:06 +01:00
|
|
|
l = sizeof(protocol);
|
|
|
|
r = getsockopt(fd, SOL_SOCKET, SO_PROTOCOL, &protocol, &l);
|
|
|
|
if (r < 0)
|
|
|
|
return r;
|
|
|
|
|
2015-06-13 21:25:05 +02:00
|
|
|
rtnl->fd = fd;
|
2017-12-18 15:17:06 +01:00
|
|
|
rtnl->protocol = protocol;
|
2013-10-21 20:12:52 +02:00
|
|
|
|
2019-11-30 07:01:06 +01:00
|
|
|
r = setsockopt_int(fd, SOL_NETLINK, NETLINK_EXT_ACK, 1);
|
|
|
|
if (r < 0)
|
|
|
|
log_debug_errno(r, "sd-netlink: Failed to enable NETLINK_EXT_ACK option, ignoring: %m");
|
|
|
|
|
2015-06-13 21:25:05 +02:00
|
|
|
r = socket_bind(rtnl);
|
2016-02-01 22:13:45 +01:00
|
|
|
if (r < 0) {
|
|
|
|
rtnl->fd = -1; /* on failure, the caller remains owner of the fd, hence don't close it here */
|
2017-12-18 15:17:06 +01:00
|
|
|
rtnl->protocol = -1;
|
2015-06-13 21:25:05 +02:00
|
|
|
return r;
|
2016-02-01 22:13:45 +01:00
|
|
|
}
|
2015-01-13 13:51:51 +01:00
|
|
|
|
2018-04-05 07:26:26 +02:00
|
|
|
*ret = TAKE_PTR(rtnl);
|
2013-10-21 20:12:52 +02:00
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2017-12-18 15:17:06 +01:00
|
|
|
int netlink_open_family(sd_netlink **ret, int family) {
|
2015-06-11 15:55:37 +02:00
|
|
|
_cleanup_close_ int fd = -1;
|
2015-01-13 13:51:51 +01:00
|
|
|
int r;
|
|
|
|
|
2017-12-18 15:17:06 +01:00
|
|
|
fd = socket_open(family);
|
2015-01-13 13:51:51 +01:00
|
|
|
if (fd < 0)
|
2015-06-13 21:25:05 +02:00
|
|
|
return fd;
|
2015-01-13 13:51:51 +01:00
|
|
|
|
2015-06-12 16:31:33 +02:00
|
|
|
r = sd_netlink_open_fd(ret, fd);
|
2015-06-11 15:55:37 +02:00
|
|
|
if (r < 0)
|
2015-01-13 13:51:51 +01:00
|
|
|
return r;
|
2015-06-11 15:55:37 +02:00
|
|
|
|
|
|
|
fd = -1;
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2017-12-18 15:17:06 +01:00
|
|
|
int sd_netlink_open(sd_netlink **ret) {
|
|
|
|
return netlink_open_family(ret, NETLINK_ROUTE);
|
|
|
|
}
|
|
|
|
|
2016-06-03 19:20:00 +02:00
|
|
|
int sd_netlink_inc_rcvbuf(sd_netlink *rtnl, size_t size) {
|
|
|
|
assert_return(rtnl, -EINVAL);
|
|
|
|
assert_return(!rtnl_pid_changed(rtnl), -ECHILD);
|
|
|
|
|
2014-11-27 18:50:48 +01:00
|
|
|
return fd_inc_rcvbuf(rtnl->fd, size);
|
|
|
|
}
|
|
|
|
|
2018-08-27 07:00:01 +02:00
|
|
|
static sd_netlink *netlink_free(sd_netlink *rtnl) {
|
2018-10-15 09:06:12 +02:00
|
|
|
sd_netlink_slot *s;
|
2018-08-27 07:00:01 +02:00
|
|
|
unsigned i;
|
2014-03-23 15:33:24 +01:00
|
|
|
|
2018-08-27 07:00:01 +02:00
|
|
|
assert(rtnl);
|
2013-11-12 22:37:51 +01:00
|
|
|
|
2018-08-27 07:00:01 +02:00
|
|
|
for (i = 0; i < rtnl->rqueue_size; i++)
|
|
|
|
sd_netlink_message_unref(rtnl->rqueue[i]);
|
|
|
|
free(rtnl->rqueue);
|
2013-11-30 01:24:29 +01:00
|
|
|
|
2018-08-27 07:00:01 +02:00
|
|
|
for (i = 0; i < rtnl->rqueue_partial_size; i++)
|
|
|
|
sd_netlink_message_unref(rtnl->rqueue_partial[i]);
|
|
|
|
free(rtnl->rqueue_partial);
|
2014-04-17 21:32:25 +02:00
|
|
|
|
2018-08-27 07:00:01 +02:00
|
|
|
free(rtnl->rbuffer);
|
2014-04-14 17:20:51 +02:00
|
|
|
|
2018-10-15 09:06:12 +02:00
|
|
|
while ((s = rtnl->slots)) {
|
|
|
|
assert(s->floating);
|
|
|
|
netlink_slot_disconnect(s, true);
|
2018-10-06 06:38:13 +02:00
|
|
|
}
|
|
|
|
hashmap_free(rtnl->reply_callbacks);
|
2018-08-27 07:00:01 +02:00
|
|
|
prioq_free(rtnl->reply_callbacks_prioq);
|
sd-rtnl: fix self-reference leaks
Like sd-bus, sd-rtnl can have self-references through queued messages. In
particular, each queued message has the following self-ref loop:
rtnl->wqueue[i]->rtnl == rtnl
Same is true for "rqueue".
When sd_rtnl_unref() gets called, we must therefore make sure we correctly
consider each self-reference when deciding to destroy the object. For each
queued message, there _might_ be one ref. However, rtnl-messages can be
created _without_ a bus-reference, therefore we need to verify the
actually required ref-count.
Once we know exactly how many self-refs exist, and we verified none of the
queued messages has external references, we can destruct the object.
We must immediately drop our own reference, then flush all queues and
destroy the bus object. Otherwise, each sd_rtnl_message_unref() call would
recurse into the same destruction logic as they enter with the same
rtnl-refcnt.
Note: We really should verify _all_ queued messages have m->rtnl set to
the bus they're queued on. If that's given, we can change:
if (REFCNT_GET(rtnl->n_ref) <= refs)
to
if (REFCNT_GET(rtnl->n_ref) == refs)
and thus avoid recalculating the required refs for each message we
remove from the queue during destruction.
2014-03-22 17:43:30 +01:00
|
|
|
|
2018-08-27 07:00:01 +02:00
|
|
|
sd_event_source_unref(rtnl->io_event_source);
|
|
|
|
sd_event_source_unref(rtnl->time_event_source);
|
|
|
|
sd_event_unref(rtnl->event);
|
sd-rtnl: fix self-reference leaks
Like sd-bus, sd-rtnl can have self-references through queued messages. In
particular, each queued message has the following self-ref loop:
rtnl->wqueue[i]->rtnl == rtnl
Same is true for "rqueue".
When sd_rtnl_unref() gets called, we must therefore make sure we correctly
consider each self-reference when deciding to destroy the object. For each
queued message, there _might_ be one ref. However, rtnl-messages can be
created _without_ a bus-reference, therefore we need to verify the
actually required ref-count.
Once we know exactly how many self-refs exist, and we verified none of the
queued messages has external references, we can destruct the object.
We must immediately drop our own reference, then flush all queues and
destroy the bus object. Otherwise, each sd_rtnl_message_unref() call would
recurse into the same destruction logic as they enter with the same
rtnl-refcnt.
Note: We really should verify _all_ queued messages have m->rtnl set to
the bus they're queued on. If that's given, we can change:
if (REFCNT_GET(rtnl->n_ref) <= refs)
to
if (REFCNT_GET(rtnl->n_ref) == refs)
and thus avoid recalculating the required refs for each message we
remove from the queue during destruction.
2014-03-22 17:43:30 +01:00
|
|
|
|
2018-08-27 07:00:01 +02:00
|
|
|
hashmap_free(rtnl->broadcast_group_refs);
|
2015-10-15 17:59:10 +02:00
|
|
|
|
2019-07-23 08:54:06 +02:00
|
|
|
hashmap_free(rtnl->genl_family_to_nlmsg_type);
|
|
|
|
hashmap_free(rtnl->nlmsg_type_to_genl_family);
|
|
|
|
|
2018-08-27 07:00:01 +02:00
|
|
|
safe_close(rtnl->fd);
|
|
|
|
return mfree(rtnl);
|
2013-10-21 20:12:52 +02:00
|
|
|
}
|
|
|
|
|
2019-03-01 18:31:45 +01:00
|
|
|
DEFINE_TRIVIAL_REF_UNREF_FUNC(sd_netlink, sd_netlink, netlink_free);
|
2018-08-27 07:00:01 +02:00
|
|
|
|
2015-06-12 16:31:33 +02:00
|
|
|
static void rtnl_seal_message(sd_netlink *rtnl, sd_netlink_message *m) {
|
2014-03-23 21:45:46 +01:00
|
|
|
assert(rtnl);
|
|
|
|
assert(!rtnl_pid_changed(rtnl));
|
|
|
|
assert(m);
|
|
|
|
assert(m->hdr);
|
|
|
|
|
2015-03-13 16:12:57 +01:00
|
|
|
/* don't use seq == 0, as that is used for broadcasts, so we
|
|
|
|
would get confused by replies to such messages */
|
2015-03-13 15:49:07 +01:00
|
|
|
m->hdr->nlmsg_seq = rtnl->serial++ ? : rtnl->serial++;
|
2014-03-23 21:45:46 +01:00
|
|
|
|
|
|
|
rtnl_message_seal(m);
|
|
|
|
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2015-06-12 16:31:33 +02:00
|
|
|
int sd_netlink_send(sd_netlink *nl,
|
2018-08-27 06:57:09 +02:00
|
|
|
sd_netlink_message *message,
|
|
|
|
uint32_t *serial) {
|
2013-11-12 22:37:51 +01:00
|
|
|
int r;
|
2013-10-21 20:12:52 +02:00
|
|
|
|
|
|
|
assert_return(nl, -EINVAL);
|
2013-10-29 17:38:31 +01:00
|
|
|
assert_return(!rtnl_pid_changed(nl), -ECHILD);
|
2013-10-21 20:12:52 +02:00
|
|
|
assert_return(message, -EINVAL);
|
2014-03-23 21:45:46 +01:00
|
|
|
assert_return(!message->sealed, -EPERM);
|
2013-10-21 20:12:52 +02:00
|
|
|
|
2014-03-23 21:45:46 +01:00
|
|
|
rtnl_seal_message(nl, message);
|
2013-10-21 20:12:52 +02:00
|
|
|
|
2015-06-13 20:51:56 +02:00
|
|
|
r = socket_write_message(nl, message);
|
|
|
|
if (r < 0)
|
|
|
|
return r;
|
2013-10-21 20:12:52 +02:00
|
|
|
|
2013-11-12 22:37:51 +01:00
|
|
|
if (serial)
|
2014-01-29 21:20:30 +01:00
|
|
|
*serial = rtnl_message_get_serial(message);
|
2013-10-21 20:12:52 +02:00
|
|
|
|
2013-11-12 22:37:51 +01:00
|
|
|
return 1;
|
|
|
|
}
|
2013-10-21 20:12:52 +02:00
|
|
|
|
2015-06-12 16:31:33 +02:00
|
|
|
int rtnl_rqueue_make_room(sd_netlink *rtnl) {
|
2014-04-10 19:40:48 +02:00
|
|
|
assert(rtnl);
|
|
|
|
|
2018-11-20 23:40:44 +01:00
|
|
|
if (rtnl->rqueue_size >= RTNL_RQUEUE_MAX)
|
|
|
|
return log_debug_errno(SYNTHETIC_ERRNO(ENOBUFS),
|
|
|
|
"rtnl: exhausted the read queue size (%d)",
|
|
|
|
RTNL_RQUEUE_MAX);
|
2014-04-10 19:40:48 +02:00
|
|
|
|
|
|
|
if (!GREEDY_REALLOC(rtnl->rqueue, rtnl->rqueue_allocated, rtnl->rqueue_size + 1))
|
|
|
|
return -ENOMEM;
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2015-06-12 16:31:33 +02:00
|
|
|
int rtnl_rqueue_partial_make_room(sd_netlink *rtnl) {
|
2014-04-17 21:32:25 +02:00
|
|
|
assert(rtnl);
|
|
|
|
|
2018-11-20 23:40:44 +01:00
|
|
|
if (rtnl->rqueue_partial_size >= RTNL_RQUEUE_MAX)
|
|
|
|
return log_debug_errno(SYNTHETIC_ERRNO(ENOBUFS),
|
|
|
|
"rtnl: exhausted the partial read queue size (%d)",
|
|
|
|
RTNL_RQUEUE_MAX);
|
2014-04-17 21:32:25 +02:00
|
|
|
|
|
|
|
if (!GREEDY_REALLOC(rtnl->rqueue_partial, rtnl->rqueue_partial_allocated,
|
|
|
|
rtnl->rqueue_partial_size + 1))
|
|
|
|
return -ENOMEM;
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2015-06-12 16:31:33 +02:00
|
|
|
static int dispatch_rqueue(sd_netlink *rtnl, sd_netlink_message **message) {
|
2013-11-12 22:37:51 +01:00
|
|
|
int r;
|
2013-10-21 20:12:52 +02:00
|
|
|
|
2013-11-12 22:37:51 +01:00
|
|
|
assert(rtnl);
|
|
|
|
assert(message);
|
|
|
|
|
2014-04-10 19:40:48 +02:00
|
|
|
if (rtnl->rqueue_size <= 0) {
|
|
|
|
/* Try to read a new message */
|
|
|
|
r = socket_read_message(rtnl);
|
2017-02-21 16:25:02 +01:00
|
|
|
if (r == -ENOBUFS) { /* FIXME: ignore buffer overruns for now */
|
|
|
|
log_debug_errno(r, "Got ENOBUFS from netlink socket, ignoring.");
|
|
|
|
return 1;
|
|
|
|
}
|
2014-04-10 19:40:48 +02:00
|
|
|
if (r <= 0)
|
|
|
|
return r;
|
2013-11-12 22:37:51 +01:00
|
|
|
}
|
|
|
|
|
2014-04-10 19:40:48 +02:00
|
|
|
/* Dispatch a queued message */
|
|
|
|
*message = rtnl->rqueue[0];
|
2016-02-23 05:32:04 +01:00
|
|
|
rtnl->rqueue_size--;
|
2015-06-12 16:31:33 +02:00
|
|
|
memmove(rtnl->rqueue, rtnl->rqueue + 1, sizeof(sd_netlink_message*) * rtnl->rqueue_size);
|
2013-11-12 22:37:51 +01:00
|
|
|
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
2015-06-12 16:31:33 +02:00
|
|
|
static int process_timeout(sd_netlink *rtnl) {
|
tree-wide: expose "p"-suffix unref calls in public APIs to make gcc cleanup easy
GLIB has recently started to officially support the gcc cleanup
attribute in its public API, hence let's do the same for our APIs.
With this patch we'll define an xyz_unrefp() call for each public
xyz_unref() call, to make it easy to use inside a
__attribute__((cleanup())) expression. Then, all code is ported over to
make use of this.
The new calls are also documented in the man pages, with examples how to
use them (well, I only added docs where the _unref() call itself already
had docs, and the examples, only cover sd_bus_unrefp() and
sd_event_unrefp()).
This also renames sd_lldp_free() to sd_lldp_unref(), since that's how we
tend to call our destructors these days.
Note that this defines no public macro that wraps gcc's attribute and
makes it easier to use. While I think it's our duty in the library to
make our stuff easy to use, I figure it's not our duty to make gcc's own
features easy to use on its own. Most likely, client code which wants to
make use of this should define its own:
#define _cleanup_(function) __attribute__((cleanup(function)))
Or similar, to make the gcc feature easier to use.
Making this logic public has the benefit that we can remove three header
files whose only purpose was to define these functions internally.
See #2008.
2015-11-27 19:13:45 +01:00
|
|
|
_cleanup_(sd_netlink_message_unrefp) sd_netlink_message *m = NULL;
|
2013-11-13 23:18:20 +01:00
|
|
|
struct reply_callback *c;
|
2018-10-15 09:06:12 +02:00
|
|
|
sd_netlink_slot *slot;
|
2013-11-13 23:18:20 +01:00
|
|
|
usec_t n;
|
|
|
|
int r;
|
|
|
|
|
|
|
|
assert(rtnl);
|
|
|
|
|
|
|
|
c = prioq_peek(rtnl->reply_callbacks_prioq);
|
|
|
|
if (!c)
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
n = now(CLOCK_MONOTONIC);
|
|
|
|
if (c->timeout > n)
|
|
|
|
return 0;
|
|
|
|
|
2017-12-18 15:17:06 +01:00
|
|
|
r = rtnl_message_new_synthetic_error(rtnl, -ETIMEDOUT, c->serial, &m);
|
2013-11-13 23:18:20 +01:00
|
|
|
if (r < 0)
|
|
|
|
return r;
|
|
|
|
|
|
|
|
assert_se(prioq_pop(rtnl->reply_callbacks_prioq) == c);
|
2018-10-15 09:06:12 +02:00
|
|
|
c->timeout = 0;
|
2013-11-13 23:18:20 +01:00
|
|
|
hashmap_remove(rtnl->reply_callbacks, &c->serial);
|
|
|
|
|
2018-10-15 09:06:12 +02:00
|
|
|
slot = container_of(c, sd_netlink_slot, reply_callback);
|
|
|
|
|
|
|
|
r = c->callback(rtnl, m, slot->userdata);
|
2015-01-29 07:26:58 +01:00
|
|
|
if (r < 0)
|
2018-10-15 10:49:53 +02:00
|
|
|
log_debug_errno(r, "sd-netlink: timedout callback %s%s%sfailed: %m",
|
|
|
|
slot->description ? "'" : "",
|
|
|
|
strempty(slot->description),
|
|
|
|
slot->description ? "' " : "");
|
2015-01-29 07:26:58 +01:00
|
|
|
|
2018-10-15 09:06:12 +02:00
|
|
|
if (slot->floating)
|
|
|
|
netlink_slot_disconnect(slot, true);
|
2013-11-13 23:18:20 +01:00
|
|
|
|
2015-01-29 07:26:58 +01:00
|
|
|
return 1;
|
2013-11-13 23:18:20 +01:00
|
|
|
}
|
|
|
|
|
2015-06-12 16:31:33 +02:00
|
|
|
static int process_reply(sd_netlink *rtnl, sd_netlink_message *m) {
|
2018-10-15 09:06:12 +02:00
|
|
|
struct reply_callback *c;
|
|
|
|
sd_netlink_slot *slot;
|
2013-11-13 23:18:20 +01:00
|
|
|
uint64_t serial;
|
2015-03-18 13:06:19 +01:00
|
|
|
uint16_t type;
|
2013-11-13 23:18:20 +01:00
|
|
|
int r;
|
|
|
|
|
|
|
|
assert(rtnl);
|
|
|
|
assert(m);
|
|
|
|
|
2014-01-29 21:20:30 +01:00
|
|
|
serial = rtnl_message_get_serial(m);
|
2013-11-13 23:18:20 +01:00
|
|
|
c = hashmap_remove(rtnl->reply_callbacks, &serial);
|
|
|
|
if (!c)
|
|
|
|
return 0;
|
|
|
|
|
2018-10-15 09:06:12 +02:00
|
|
|
if (c->timeout != 0) {
|
2013-11-13 23:18:20 +01:00
|
|
|
prioq_remove(rtnl->reply_callbacks_prioq, c, &c->prioq_idx);
|
2018-10-15 09:06:12 +02:00
|
|
|
c->timeout = 0;
|
|
|
|
}
|
2013-11-13 23:18:20 +01:00
|
|
|
|
2015-06-12 16:31:33 +02:00
|
|
|
r = sd_netlink_message_get_type(m, &type);
|
2015-03-18 13:06:19 +01:00
|
|
|
if (r < 0)
|
2018-10-15 09:06:12 +02:00
|
|
|
return r;
|
2015-03-18 13:06:19 +01:00
|
|
|
|
|
|
|
if (type == NLMSG_DONE)
|
|
|
|
m = NULL;
|
|
|
|
|
2018-10-15 09:06:12 +02:00
|
|
|
slot = container_of(c, sd_netlink_slot, reply_callback);
|
|
|
|
|
|
|
|
r = c->callback(rtnl, m, slot->userdata);
|
2015-01-29 07:26:58 +01:00
|
|
|
if (r < 0)
|
2018-10-15 10:49:53 +02:00
|
|
|
log_debug_errno(r, "sd-netlink: reply callback %s%s%sfailed: %m",
|
|
|
|
slot->description ? "'" : "",
|
|
|
|
strempty(slot->description),
|
|
|
|
slot->description ? "' " : "");
|
2015-01-29 07:26:58 +01:00
|
|
|
|
2018-10-15 09:06:12 +02:00
|
|
|
if (slot->floating)
|
|
|
|
netlink_slot_disconnect(slot, true);
|
2018-10-06 06:38:13 +02:00
|
|
|
|
2015-01-29 07:26:58 +01:00
|
|
|
return 1;
|
2013-11-13 23:18:20 +01:00
|
|
|
}
|
|
|
|
|
2015-06-12 16:31:33 +02:00
|
|
|
static int process_match(sd_netlink *rtnl, sd_netlink_message *m) {
|
2013-11-30 01:24:29 +01:00
|
|
|
struct match_callback *c;
|
2018-10-15 09:06:12 +02:00
|
|
|
sd_netlink_slot *slot;
|
2013-11-30 01:24:29 +01:00
|
|
|
uint16_t type;
|
|
|
|
int r;
|
|
|
|
|
|
|
|
assert(rtnl);
|
|
|
|
assert(m);
|
|
|
|
|
2015-06-12 16:31:33 +02:00
|
|
|
r = sd_netlink_message_get_type(m, &type);
|
2013-11-30 01:24:29 +01:00
|
|
|
if (r < 0)
|
|
|
|
return r;
|
|
|
|
|
|
|
|
LIST_FOREACH(match_callbacks, c, rtnl->match_callbacks) {
|
2013-12-06 15:20:36 +01:00
|
|
|
if (type == c->type) {
|
2018-10-15 09:06:12 +02:00
|
|
|
slot = container_of(c, sd_netlink_slot, match_callback);
|
|
|
|
|
|
|
|
r = c->callback(rtnl, m, slot->userdata);
|
2015-01-29 07:26:58 +01:00
|
|
|
if (r != 0) {
|
|
|
|
if (r < 0)
|
2018-10-15 10:49:53 +02:00
|
|
|
log_debug_errno(r, "sd-netlink: match callback %s%s%sfailed: %m",
|
|
|
|
slot->description ? "'" : "",
|
|
|
|
strempty(slot->description),
|
|
|
|
slot->description ? "' " : "");
|
2015-01-29 07:26:58 +01:00
|
|
|
|
|
|
|
break;
|
|
|
|
}
|
2013-11-30 01:24:29 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-01-29 07:26:58 +01:00
|
|
|
return 1;
|
2013-11-30 01:24:29 +01:00
|
|
|
}
|
|
|
|
|
2015-06-12 16:31:33 +02:00
|
|
|
static int process_running(sd_netlink *rtnl, sd_netlink_message **ret) {
|
tree-wide: expose "p"-suffix unref calls in public APIs to make gcc cleanup easy
GLIB has recently started to officially support the gcc cleanup
attribute in its public API, hence let's do the same for our APIs.
With this patch we'll define an xyz_unrefp() call for each public
xyz_unref() call, to make it easy to use inside a
__attribute__((cleanup())) expression. Then, all code is ported over to
make use of this.
The new calls are also documented in the man pages, with examples how to
use them (well, I only added docs where the _unref() call itself already
had docs, and the examples, only cover sd_bus_unrefp() and
sd_event_unrefp()).
This also renames sd_lldp_free() to sd_lldp_unref(), since that's how we
tend to call our destructors these days.
Note that this defines no public macro that wraps gcc's attribute and
makes it easier to use. While I think it's our duty in the library to
make our stuff easy to use, I figure it's not our duty to make gcc's own
features easy to use on its own. Most likely, client code which wants to
make use of this should define its own:
#define _cleanup_(function) __attribute__((cleanup(function)))
Or similar, to make the gcc feature easier to use.
Making this logic public has the benefit that we can remove three header
files whose only purpose was to define these functions internally.
See #2008.
2015-11-27 19:13:45 +01:00
|
|
|
_cleanup_(sd_netlink_message_unrefp) sd_netlink_message *m = NULL;
|
2013-11-12 22:37:51 +01:00
|
|
|
int r;
|
|
|
|
|
2013-12-06 15:13:34 +01:00
|
|
|
assert(rtnl);
|
|
|
|
|
2013-11-13 23:18:20 +01:00
|
|
|
r = process_timeout(rtnl);
|
|
|
|
if (r != 0)
|
|
|
|
goto null_message;
|
|
|
|
|
2013-11-12 22:37:51 +01:00
|
|
|
r = dispatch_rqueue(rtnl, &m);
|
|
|
|
if (r < 0)
|
|
|
|
return r;
|
|
|
|
if (!m)
|
|
|
|
goto null_message;
|
|
|
|
|
2015-06-12 16:31:33 +02:00
|
|
|
if (sd_netlink_message_is_broadcast(m)) {
|
2015-03-13 16:13:24 +01:00
|
|
|
r = process_match(rtnl, m);
|
|
|
|
if (r != 0)
|
|
|
|
goto null_message;
|
|
|
|
} else {
|
|
|
|
r = process_reply(rtnl, m);
|
|
|
|
if (r != 0)
|
|
|
|
goto null_message;
|
|
|
|
}
|
2013-11-30 01:24:29 +01:00
|
|
|
|
2013-11-12 22:37:51 +01:00
|
|
|
if (ret) {
|
2018-04-05 07:26:26 +02:00
|
|
|
*ret = TAKE_PTR(m);
|
2013-11-12 22:37:51 +01:00
|
|
|
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
return 1;
|
|
|
|
|
|
|
|
null_message:
|
|
|
|
if (r >= 0 && ret)
|
|
|
|
*ret = NULL;
|
|
|
|
|
|
|
|
return r;
|
|
|
|
}
|
2013-11-13 23:18:20 +01:00
|
|
|
|
2015-06-12 16:31:33 +02:00
|
|
|
int sd_netlink_process(sd_netlink *rtnl, sd_netlink_message **ret) {
|
tree-wide: expose "p"-suffix unref calls in public APIs to make gcc cleanup easy
GLIB has recently started to officially support the gcc cleanup
attribute in its public API, hence let's do the same for our APIs.
With this patch we'll define an xyz_unrefp() call for each public
xyz_unref() call, to make it easy to use inside a
__attribute__((cleanup())) expression. Then, all code is ported over to
make use of this.
The new calls are also documented in the man pages, with examples how to
use them (well, I only added docs where the _unref() call itself already
had docs, and the examples, only cover sd_bus_unrefp() and
sd_event_unrefp()).
This also renames sd_lldp_free() to sd_lldp_unref(), since that's how we
tend to call our destructors these days.
Note that this defines no public macro that wraps gcc's attribute and
makes it easier to use. While I think it's our duty in the library to
make our stuff easy to use, I figure it's not our duty to make gcc's own
features easy to use on its own. Most likely, client code which wants to
make use of this should define its own:
#define _cleanup_(function) __attribute__((cleanup(function)))
Or similar, to make the gcc feature easier to use.
Making this logic public has the benefit that we can remove three header
files whose only purpose was to define these functions internally.
See #2008.
2015-11-27 19:13:45 +01:00
|
|
|
NETLINK_DONT_DESTROY(rtnl);
|
2013-11-12 22:37:51 +01:00
|
|
|
int r;
|
|
|
|
|
|
|
|
assert_return(rtnl, -EINVAL);
|
|
|
|
assert_return(!rtnl_pid_changed(rtnl), -ECHILD);
|
|
|
|
assert_return(!rtnl->processing, -EBUSY);
|
|
|
|
|
|
|
|
rtnl->processing = true;
|
|
|
|
r = process_running(rtnl, ret);
|
|
|
|
rtnl->processing = false;
|
|
|
|
|
|
|
|
return r;
|
|
|
|
}
|
|
|
|
|
|
|
|
static usec_t calc_elapse(uint64_t usec) {
|
|
|
|
if (usec == (uint64_t) -1)
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
if (usec == 0)
|
|
|
|
usec = RTNL_DEFAULT_TIMEOUT;
|
|
|
|
|
|
|
|
return now(CLOCK_MONOTONIC) + usec;
|
|
|
|
}
|
|
|
|
|
2015-06-12 16:31:33 +02:00
|
|
|
static int rtnl_poll(sd_netlink *rtnl, bool need_more, uint64_t timeout_usec) {
|
2014-07-29 12:23:31 +02:00
|
|
|
usec_t m = USEC_INFINITY;
|
2013-11-14 19:23:39 +01:00
|
|
|
int r, e;
|
|
|
|
|
|
|
|
assert(rtnl);
|
2013-11-12 22:37:51 +01:00
|
|
|
|
2015-06-12 16:31:33 +02:00
|
|
|
e = sd_netlink_get_events(rtnl);
|
2013-11-14 19:23:39 +01:00
|
|
|
if (e < 0)
|
|
|
|
return e;
|
2013-11-12 22:37:51 +01:00
|
|
|
|
2013-11-14 19:23:39 +01:00
|
|
|
if (need_more)
|
|
|
|
/* Caller wants more data, and doesn't care about
|
|
|
|
* what's been read or any other timeouts. */
|
2014-12-29 01:50:07 +01:00
|
|
|
e |= POLLIN;
|
2013-11-14 19:23:39 +01:00
|
|
|
else {
|
|
|
|
usec_t until;
|
|
|
|
/* Caller wants to process if there is something to
|
|
|
|
* process, but doesn't care otherwise */
|
|
|
|
|
2015-06-12 16:31:33 +02:00
|
|
|
r = sd_netlink_get_timeout(rtnl, &until);
|
2013-11-14 19:23:39 +01:00
|
|
|
if (r < 0)
|
|
|
|
return r;
|
|
|
|
if (r > 0) {
|
|
|
|
usec_t nw;
|
|
|
|
nw = now(CLOCK_MONOTONIC);
|
|
|
|
m = until > nw ? until - nw : 0;
|
|
|
|
}
|
|
|
|
}
|
2013-10-21 20:12:52 +02:00
|
|
|
|
2020-06-10 11:43:40 +02:00
|
|
|
if (timeout_usec != (uint64_t) -1 && (m == USEC_INFINITY || timeout_usec < m))
|
2013-11-14 19:23:39 +01:00
|
|
|
m = timeout_usec;
|
|
|
|
|
2020-06-10 11:43:40 +02:00
|
|
|
r = fd_wait_for_event(rtnl->fd, e, m);
|
2020-09-14 16:18:02 +02:00
|
|
|
if (r <= 0)
|
2020-06-10 11:43:40 +02:00
|
|
|
return r;
|
2013-11-12 22:37:51 +01:00
|
|
|
|
2020-06-09 13:40:25 +02:00
|
|
|
return 1;
|
2013-11-12 22:37:51 +01:00
|
|
|
}
|
|
|
|
|
2015-06-12 16:31:33 +02:00
|
|
|
int sd_netlink_wait(sd_netlink *nl, uint64_t timeout_usec) {
|
2013-11-12 22:37:51 +01:00
|
|
|
assert_return(nl, -EINVAL);
|
|
|
|
assert_return(!rtnl_pid_changed(nl), -ECHILD);
|
|
|
|
|
|
|
|
if (nl->rqueue_size > 0)
|
|
|
|
return 0;
|
|
|
|
|
2013-11-14 19:23:39 +01:00
|
|
|
return rtnl_poll(nl, false, timeout_usec);
|
2013-11-12 22:37:51 +01:00
|
|
|
}
|
|
|
|
|
2013-11-13 23:18:20 +01:00
|
|
|
static int timeout_compare(const void *a, const void *b) {
|
|
|
|
const struct reply_callback *x = a, *y = b;
|
|
|
|
|
|
|
|
if (x->timeout != 0 && y->timeout == 0)
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
if (x->timeout == 0 && y->timeout != 0)
|
|
|
|
return 1;
|
|
|
|
|
2018-10-09 12:50:43 +02:00
|
|
|
return CMP(x->timeout, y->timeout);
|
2013-11-13 23:18:20 +01:00
|
|
|
}
|
|
|
|
|
2018-10-06 06:38:13 +02:00
|
|
|
int sd_netlink_call_async(
|
|
|
|
sd_netlink *nl,
|
2018-10-15 09:06:12 +02:00
|
|
|
sd_netlink_slot **ret_slot,
|
2018-10-06 06:38:13 +02:00
|
|
|
sd_netlink_message *m,
|
|
|
|
sd_netlink_message_handler_t callback,
|
|
|
|
sd_netlink_destroy_t destroy_callback,
|
|
|
|
void *userdata,
|
2018-10-15 10:49:53 +02:00
|
|
|
uint64_t usec,
|
|
|
|
const char *description) {
|
2018-10-15 09:06:12 +02:00
|
|
|
_cleanup_free_ sd_netlink_slot *slot = NULL;
|
2013-11-13 23:18:20 +01:00
|
|
|
uint32_t s;
|
|
|
|
int r, k;
|
|
|
|
|
|
|
|
assert_return(nl, -EINVAL);
|
|
|
|
assert_return(m, -EINVAL);
|
|
|
|
assert_return(callback, -EINVAL);
|
|
|
|
assert_return(!rtnl_pid_changed(nl), -ECHILD);
|
|
|
|
|
2014-08-13 01:00:18 +02:00
|
|
|
r = hashmap_ensure_allocated(&nl->reply_callbacks, &uint64_hash_ops);
|
2013-11-13 23:18:20 +01:00
|
|
|
if (r < 0)
|
|
|
|
return r;
|
|
|
|
|
|
|
|
if (usec != (uint64_t) -1) {
|
|
|
|
r = prioq_ensure_allocated(&nl->reply_callbacks_prioq, timeout_compare);
|
|
|
|
if (r < 0)
|
|
|
|
return r;
|
|
|
|
}
|
|
|
|
|
2018-12-14 02:26:36 +01:00
|
|
|
r = netlink_slot_allocate(nl, !ret_slot, NETLINK_REPLY_CALLBACK, sizeof(struct reply_callback), userdata, description, &slot);
|
2018-10-15 10:49:53 +02:00
|
|
|
if (r < 0)
|
|
|
|
return r;
|
2013-11-13 23:18:20 +01:00
|
|
|
|
2018-10-15 09:06:12 +02:00
|
|
|
slot->reply_callback.callback = callback;
|
|
|
|
slot->reply_callback.timeout = calc_elapse(usec);
|
2013-11-13 23:18:20 +01:00
|
|
|
|
2015-06-12 16:31:33 +02:00
|
|
|
k = sd_netlink_send(nl, m, &s);
|
2018-10-06 06:38:13 +02:00
|
|
|
if (k < 0)
|
2013-11-13 23:18:20 +01:00
|
|
|
return k;
|
|
|
|
|
2018-10-15 09:06:12 +02:00
|
|
|
slot->reply_callback.serial = s;
|
2013-11-13 23:18:20 +01:00
|
|
|
|
2018-10-15 09:06:12 +02:00
|
|
|
r = hashmap_put(nl->reply_callbacks, &slot->reply_callback.serial, &slot->reply_callback);
|
2018-10-06 06:38:13 +02:00
|
|
|
if (r < 0)
|
2013-11-13 23:18:20 +01:00
|
|
|
return r;
|
|
|
|
|
2018-10-15 09:06:12 +02:00
|
|
|
if (slot->reply_callback.timeout != 0) {
|
|
|
|
r = prioq_put(nl->reply_callbacks_prioq, &slot->reply_callback, &slot->reply_callback.prioq_idx);
|
2018-10-03 20:20:35 +02:00
|
|
|
if (r < 0) {
|
2018-10-15 09:06:12 +02:00
|
|
|
(void) hashmap_remove(nl->reply_callbacks, &slot->reply_callback.serial);
|
2013-11-13 23:18:20 +01:00
|
|
|
return r;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-12-14 02:26:36 +01:00
|
|
|
/* Set this at last. Otherwise, some failures in above call the destroy callback but some do not. */
|
|
|
|
slot->destroy_callback = destroy_callback;
|
|
|
|
|
2018-10-15 09:06:12 +02:00
|
|
|
if (ret_slot)
|
|
|
|
*ret_slot = slot;
|
2013-11-13 23:18:20 +01:00
|
|
|
|
2018-10-15 09:06:12 +02:00
|
|
|
TAKE_PTR(slot);
|
2018-10-06 06:38:13 +02:00
|
|
|
|
2013-11-13 23:18:20 +01:00
|
|
|
return k;
|
|
|
|
}
|
|
|
|
|
2015-06-12 16:31:33 +02:00
|
|
|
int sd_netlink_call(sd_netlink *rtnl,
|
|
|
|
sd_netlink_message *message,
|
2013-11-12 22:37:51 +01:00
|
|
|
uint64_t usec,
|
2015-06-12 16:31:33 +02:00
|
|
|
sd_netlink_message **ret) {
|
2013-11-12 22:37:51 +01:00
|
|
|
usec_t timeout;
|
|
|
|
uint32_t serial;
|
|
|
|
int r;
|
|
|
|
|
2014-04-10 19:40:48 +02:00
|
|
|
assert_return(rtnl, -EINVAL);
|
|
|
|
assert_return(!rtnl_pid_changed(rtnl), -ECHILD);
|
2013-11-12 22:37:51 +01:00
|
|
|
assert_return(message, -EINVAL);
|
|
|
|
|
2015-06-12 16:31:33 +02:00
|
|
|
r = sd_netlink_send(rtnl, message, &serial);
|
2013-11-12 22:37:51 +01:00
|
|
|
if (r < 0)
|
|
|
|
return r;
|
|
|
|
|
|
|
|
timeout = calc_elapse(usec);
|
|
|
|
|
2013-10-21 20:12:52 +02:00
|
|
|
for (;;) {
|
2013-11-12 22:37:51 +01:00
|
|
|
usec_t left;
|
2015-03-18 13:06:19 +01:00
|
|
|
unsigned i;
|
2013-10-21 20:12:52 +02:00
|
|
|
|
2015-03-18 13:06:19 +01:00
|
|
|
for (i = 0; i < rtnl->rqueue_size; i++) {
|
2014-04-10 19:40:48 +02:00
|
|
|
uint32_t received_serial;
|
2013-10-21 20:12:52 +02:00
|
|
|
|
2015-03-18 13:06:19 +01:00
|
|
|
received_serial = rtnl_message_get_serial(rtnl->rqueue[i]);
|
2013-10-21 20:12:52 +02:00
|
|
|
|
|
|
|
if (received_serial == serial) {
|
tree-wide: expose "p"-suffix unref calls in public APIs to make gcc cleanup easy
GLIB has recently started to officially support the gcc cleanup
attribute in its public API, hence let's do the same for our APIs.
With this patch we'll define an xyz_unrefp() call for each public
xyz_unref() call, to make it easy to use inside a
__attribute__((cleanup())) expression. Then, all code is ported over to
make use of this.
The new calls are also documented in the man pages, with examples how to
use them (well, I only added docs where the _unref() call itself already
had docs, and the examples, only cover sd_bus_unrefp() and
sd_event_unrefp()).
This also renames sd_lldp_free() to sd_lldp_unref(), since that's how we
tend to call our destructors these days.
Note that this defines no public macro that wraps gcc's attribute and
makes it easier to use. While I think it's our duty in the library to
make our stuff easy to use, I figure it's not our duty to make gcc's own
features easy to use on its own. Most likely, client code which wants to
make use of this should define its own:
#define _cleanup_(function) __attribute__((cleanup(function)))
Or similar, to make the gcc feature easier to use.
Making this logic public has the benefit that we can remove three header
files whose only purpose was to define these functions internally.
See #2008.
2015-11-27 19:13:45 +01:00
|
|
|
_cleanup_(sd_netlink_message_unrefp) sd_netlink_message *incoming = NULL;
|
2015-03-18 13:06:19 +01:00
|
|
|
uint16_t type;
|
|
|
|
|
|
|
|
incoming = rtnl->rqueue[i];
|
|
|
|
|
2014-04-10 19:40:48 +02:00
|
|
|
/* found a match, remove from rqueue and return it */
|
|
|
|
memmove(rtnl->rqueue + i,rtnl->rqueue + i + 1,
|
2015-06-12 16:31:33 +02:00
|
|
|
sizeof(sd_netlink_message*) * (rtnl->rqueue_size - i - 1));
|
2014-04-10 19:40:48 +02:00
|
|
|
rtnl->rqueue_size--;
|
|
|
|
|
2015-06-12 16:31:33 +02:00
|
|
|
r = sd_netlink_message_get_errno(incoming);
|
2015-03-18 13:06:19 +01:00
|
|
|
if (r < 0)
|
|
|
|
return r;
|
|
|
|
|
2015-06-12 16:31:33 +02:00
|
|
|
r = sd_netlink_message_get_type(incoming, &type);
|
2015-03-18 13:06:19 +01:00
|
|
|
if (r < 0)
|
2013-10-21 20:12:52 +02:00
|
|
|
return r;
|
2015-03-18 13:06:19 +01:00
|
|
|
|
|
|
|
if (type == NLMSG_DONE) {
|
|
|
|
*ret = NULL;
|
|
|
|
return 0;
|
2014-04-10 19:40:48 +02:00
|
|
|
}
|
2013-10-21 20:12:52 +02:00
|
|
|
|
2018-04-05 07:26:26 +02:00
|
|
|
if (ret)
|
|
|
|
*ret = TAKE_PTR(incoming);
|
2013-10-21 20:12:52 +02:00
|
|
|
|
2013-11-12 22:37:51 +01:00
|
|
|
return 1;
|
2013-10-21 20:12:52 +02:00
|
|
|
}
|
|
|
|
}
|
2014-04-10 19:40:48 +02:00
|
|
|
|
|
|
|
r = socket_read_message(rtnl);
|
|
|
|
if (r < 0)
|
|
|
|
return r;
|
|
|
|
if (r > 0)
|
2014-08-29 13:28:04 +02:00
|
|
|
/* received message, so try to process straight away */
|
2013-11-12 22:37:51 +01:00
|
|
|
continue;
|
2013-10-21 20:12:52 +02:00
|
|
|
|
2013-11-12 22:37:51 +01:00
|
|
|
if (timeout > 0) {
|
|
|
|
usec_t n;
|
|
|
|
|
|
|
|
n = now(CLOCK_MONOTONIC);
|
|
|
|
if (n >= timeout)
|
|
|
|
return -ETIMEDOUT;
|
|
|
|
|
|
|
|
left = timeout - n;
|
|
|
|
} else
|
|
|
|
left = (uint64_t) -1;
|
|
|
|
|
2014-04-10 19:40:48 +02:00
|
|
|
r = rtnl_poll(rtnl, true, left);
|
2013-11-12 22:37:51 +01:00
|
|
|
if (r < 0)
|
|
|
|
return r;
|
2014-12-29 01:54:04 +01:00
|
|
|
else if (r == 0)
|
|
|
|
return -ETIMEDOUT;
|
2013-11-14 19:23:39 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-10-24 11:40:44 +02:00
|
|
|
int sd_netlink_get_events(const sd_netlink *rtnl) {
|
2013-11-14 19:23:39 +01:00
|
|
|
assert_return(rtnl, -EINVAL);
|
|
|
|
assert_return(!rtnl_pid_changed(rtnl), -ECHILD);
|
|
|
|
|
2015-06-13 20:51:56 +02:00
|
|
|
if (rtnl->rqueue_size == 0)
|
|
|
|
return POLLIN;
|
|
|
|
else
|
|
|
|
return 0;
|
2013-11-14 19:23:39 +01:00
|
|
|
}
|
|
|
|
|
2019-10-24 11:40:44 +02:00
|
|
|
int sd_netlink_get_timeout(const sd_netlink *rtnl, uint64_t *timeout_usec) {
|
2013-11-14 19:23:39 +01:00
|
|
|
struct reply_callback *c;
|
|
|
|
|
|
|
|
assert_return(rtnl, -EINVAL);
|
|
|
|
assert_return(timeout_usec, -EINVAL);
|
|
|
|
assert_return(!rtnl_pid_changed(rtnl), -ECHILD);
|
|
|
|
|
|
|
|
if (rtnl->rqueue_size > 0) {
|
|
|
|
*timeout_usec = 0;
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
c = prioq_peek(rtnl->reply_callbacks_prioq);
|
|
|
|
if (!c) {
|
|
|
|
*timeout_usec = (uint64_t) -1;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
*timeout_usec = c->timeout;
|
|
|
|
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int io_callback(sd_event_source *s, int fd, uint32_t revents, void *userdata) {
|
2015-06-12 16:31:33 +02:00
|
|
|
sd_netlink *rtnl = userdata;
|
2013-11-14 19:23:39 +01:00
|
|
|
int r;
|
|
|
|
|
|
|
|
assert(rtnl);
|
|
|
|
|
2015-06-12 16:31:33 +02:00
|
|
|
r = sd_netlink_process(rtnl, NULL);
|
2013-11-14 19:23:39 +01:00
|
|
|
if (r < 0)
|
|
|
|
return r;
|
|
|
|
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int time_callback(sd_event_source *s, uint64_t usec, void *userdata) {
|
2015-06-12 16:31:33 +02:00
|
|
|
sd_netlink *rtnl = userdata;
|
2013-11-14 19:23:39 +01:00
|
|
|
int r;
|
|
|
|
|
|
|
|
assert(rtnl);
|
|
|
|
|
2015-06-12 16:31:33 +02:00
|
|
|
r = sd_netlink_process(rtnl, NULL);
|
2013-11-14 19:23:39 +01:00
|
|
|
if (r < 0)
|
|
|
|
return r;
|
|
|
|
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int prepare_callback(sd_event_source *s, void *userdata) {
|
2015-06-12 16:31:33 +02:00
|
|
|
sd_netlink *rtnl = userdata;
|
2013-11-14 19:23:39 +01:00
|
|
|
int r, e;
|
|
|
|
usec_t until;
|
|
|
|
|
|
|
|
assert(s);
|
|
|
|
assert(rtnl);
|
|
|
|
|
2015-06-12 16:31:33 +02:00
|
|
|
e = sd_netlink_get_events(rtnl);
|
2013-11-14 19:23:39 +01:00
|
|
|
if (e < 0)
|
|
|
|
return e;
|
|
|
|
|
|
|
|
r = sd_event_source_set_io_events(rtnl->io_event_source, e);
|
|
|
|
if (r < 0)
|
|
|
|
return r;
|
|
|
|
|
2015-06-12 16:31:33 +02:00
|
|
|
r = sd_netlink_get_timeout(rtnl, &until);
|
2013-11-14 19:23:39 +01:00
|
|
|
if (r < 0)
|
|
|
|
return r;
|
|
|
|
if (r > 0) {
|
|
|
|
int j;
|
|
|
|
|
|
|
|
j = sd_event_source_set_time(rtnl->time_event_source, until);
|
|
|
|
if (j < 0)
|
|
|
|
return j;
|
|
|
|
}
|
|
|
|
|
|
|
|
r = sd_event_source_set_enabled(rtnl->time_event_source, r > 0);
|
|
|
|
if (r < 0)
|
|
|
|
return r;
|
|
|
|
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
2016-02-16 19:33:36 +01:00
|
|
|
int sd_netlink_attach_event(sd_netlink *rtnl, sd_event *event, int64_t priority) {
|
2013-11-14 19:23:39 +01:00
|
|
|
int r;
|
|
|
|
|
|
|
|
assert_return(rtnl, -EINVAL);
|
|
|
|
assert_return(!rtnl->event, -EBUSY);
|
|
|
|
|
|
|
|
assert(!rtnl->io_event_source);
|
|
|
|
assert(!rtnl->time_event_source);
|
|
|
|
|
|
|
|
if (event)
|
|
|
|
rtnl->event = sd_event_ref(event);
|
|
|
|
else {
|
|
|
|
r = sd_event_default(&rtnl->event);
|
|
|
|
if (r < 0)
|
|
|
|
return r;
|
|
|
|
}
|
|
|
|
|
2014-02-19 23:54:58 +01:00
|
|
|
r = sd_event_add_io(rtnl->event, &rtnl->io_event_source, rtnl->fd, 0, io_callback, rtnl);
|
2013-11-14 19:23:39 +01:00
|
|
|
if (r < 0)
|
|
|
|
goto fail;
|
|
|
|
|
|
|
|
r = sd_event_source_set_priority(rtnl->io_event_source, priority);
|
|
|
|
if (r < 0)
|
|
|
|
goto fail;
|
|
|
|
|
2014-11-04 16:27:05 +01:00
|
|
|
r = sd_event_source_set_description(rtnl->io_event_source, "rtnl-receive-message");
|
2014-08-28 15:46:29 +02:00
|
|
|
if (r < 0)
|
|
|
|
goto fail;
|
|
|
|
|
2013-11-14 19:23:39 +01:00
|
|
|
r = sd_event_source_set_prepare(rtnl->io_event_source, prepare_callback);
|
|
|
|
if (r < 0)
|
|
|
|
goto fail;
|
|
|
|
|
2014-03-24 02:49:09 +01:00
|
|
|
r = sd_event_add_time(rtnl->event, &rtnl->time_event_source, CLOCK_MONOTONIC, 0, 0, time_callback, rtnl);
|
2013-11-14 19:23:39 +01:00
|
|
|
if (r < 0)
|
|
|
|
goto fail;
|
|
|
|
|
|
|
|
r = sd_event_source_set_priority(rtnl->time_event_source, priority);
|
|
|
|
if (r < 0)
|
|
|
|
goto fail;
|
|
|
|
|
2014-11-04 16:27:05 +01:00
|
|
|
r = sd_event_source_set_description(rtnl->time_event_source, "rtnl-timer");
|
2014-08-28 15:46:29 +02:00
|
|
|
if (r < 0)
|
|
|
|
goto fail;
|
|
|
|
|
2013-11-14 19:23:39 +01:00
|
|
|
return 0;
|
|
|
|
|
|
|
|
fail:
|
2015-06-12 16:31:33 +02:00
|
|
|
sd_netlink_detach_event(rtnl);
|
2013-11-14 19:23:39 +01:00
|
|
|
return r;
|
|
|
|
}
|
|
|
|
|
2015-06-12 16:31:33 +02:00
|
|
|
int sd_netlink_detach_event(sd_netlink *rtnl) {
|
2013-11-14 19:23:39 +01:00
|
|
|
assert_return(rtnl, -EINVAL);
|
|
|
|
assert_return(rtnl->event, -ENXIO);
|
|
|
|
|
2015-06-13 20:51:56 +02:00
|
|
|
rtnl->io_event_source = sd_event_source_unref(rtnl->io_event_source);
|
2013-11-14 19:23:39 +01:00
|
|
|
|
2015-06-13 20:51:56 +02:00
|
|
|
rtnl->time_event_source = sd_event_source_unref(rtnl->time_event_source);
|
2013-11-14 19:23:39 +01:00
|
|
|
|
2015-06-13 20:51:56 +02:00
|
|
|
rtnl->event = sd_event_unref(rtnl->event);
|
2013-11-14 19:23:39 +01:00
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
2013-11-30 01:24:29 +01:00
|
|
|
|
2018-10-15 09:06:12 +02:00
|
|
|
int sd_netlink_add_match(
|
|
|
|
sd_netlink *rtnl,
|
|
|
|
sd_netlink_slot **ret_slot,
|
|
|
|
uint16_t type,
|
|
|
|
sd_netlink_message_handler_t callback,
|
|
|
|
sd_netlink_destroy_t destroy_callback,
|
2018-10-15 10:49:53 +02:00
|
|
|
void *userdata,
|
|
|
|
const char *description) {
|
2018-10-15 09:06:12 +02:00
|
|
|
_cleanup_free_ sd_netlink_slot *slot = NULL;
|
2015-06-11 15:55:37 +02:00
|
|
|
int r;
|
2013-11-30 01:24:29 +01:00
|
|
|
|
|
|
|
assert_return(rtnl, -EINVAL);
|
|
|
|
assert_return(callback, -EINVAL);
|
|
|
|
assert_return(!rtnl_pid_changed(rtnl), -ECHILD);
|
|
|
|
|
2018-12-14 02:26:36 +01:00
|
|
|
r = netlink_slot_allocate(rtnl, !ret_slot, NETLINK_MATCH_CALLBACK, sizeof(struct match_callback), userdata, description, &slot);
|
2018-10-15 10:49:53 +02:00
|
|
|
if (r < 0)
|
|
|
|
return r;
|
2013-11-30 01:24:29 +01:00
|
|
|
|
2018-10-15 09:06:12 +02:00
|
|
|
slot->match_callback.callback = callback;
|
|
|
|
slot->match_callback.type = type;
|
2013-11-30 01:24:29 +01:00
|
|
|
|
2015-06-11 15:55:37 +02:00
|
|
|
switch (type) {
|
|
|
|
case RTM_NEWLINK:
|
|
|
|
case RTM_DELLINK:
|
2015-10-15 17:59:10 +02:00
|
|
|
r = socket_broadcast_group_ref(rtnl, RTNLGRP_LINK);
|
2015-06-11 15:55:37 +02:00
|
|
|
if (r < 0)
|
|
|
|
return r;
|
|
|
|
|
|
|
|
break;
|
|
|
|
case RTM_NEWADDR:
|
|
|
|
case RTM_DELADDR:
|
2015-10-15 17:59:10 +02:00
|
|
|
r = socket_broadcast_group_ref(rtnl, RTNLGRP_IPV4_IFADDR);
|
2015-06-11 15:55:37 +02:00
|
|
|
if (r < 0)
|
|
|
|
return r;
|
|
|
|
|
2015-10-15 17:59:10 +02:00
|
|
|
r = socket_broadcast_group_ref(rtnl, RTNLGRP_IPV6_IFADDR);
|
2015-06-11 15:55:37 +02:00
|
|
|
if (r < 0)
|
|
|
|
return r;
|
|
|
|
|
2019-04-19 09:53:34 +02:00
|
|
|
break;
|
|
|
|
case RTM_NEWNEIGH:
|
|
|
|
case RTM_DELNEIGH:
|
|
|
|
r = socket_broadcast_group_ref(rtnl, RTNLGRP_NEIGH);
|
|
|
|
if (r < 0)
|
|
|
|
return r;
|
|
|
|
|
2015-06-11 15:55:37 +02:00
|
|
|
break;
|
2015-10-09 21:37:04 +02:00
|
|
|
case RTM_NEWROUTE:
|
|
|
|
case RTM_DELROUTE:
|
2015-10-15 17:59:10 +02:00
|
|
|
r = socket_broadcast_group_ref(rtnl, RTNLGRP_IPV4_ROUTE);
|
2015-10-09 21:37:04 +02:00
|
|
|
if (r < 0)
|
|
|
|
return r;
|
|
|
|
|
2015-10-15 17:59:10 +02:00
|
|
|
r = socket_broadcast_group_ref(rtnl, RTNLGRP_IPV6_ROUTE);
|
2015-10-09 21:37:04 +02:00
|
|
|
if (r < 0)
|
|
|
|
return r;
|
|
|
|
break;
|
2017-09-14 21:51:39 +02:00
|
|
|
case RTM_NEWRULE:
|
|
|
|
case RTM_DELRULE:
|
|
|
|
r = socket_broadcast_group_ref(rtnl, RTNLGRP_IPV4_RULE);
|
|
|
|
if (r < 0)
|
|
|
|
return r;
|
|
|
|
|
|
|
|
r = socket_broadcast_group_ref(rtnl, RTNLGRP_IPV6_RULE);
|
|
|
|
if (r < 0)
|
|
|
|
return r;
|
|
|
|
break;
|
2019-10-04 21:40:51 +02:00
|
|
|
case RTM_NEWNEXTHOP:
|
|
|
|
case RTM_DELNEXTHOP:
|
|
|
|
r = socket_broadcast_group_ref(rtnl, RTNLGRP_NEXTHOP);
|
|
|
|
if (r < 0)
|
|
|
|
return r;
|
|
|
|
break;
|
|
|
|
|
2015-06-11 15:55:37 +02:00
|
|
|
default:
|
|
|
|
return -EOPNOTSUPP;
|
|
|
|
}
|
|
|
|
|
2018-10-15 09:06:12 +02:00
|
|
|
LIST_PREPEND(match_callbacks, rtnl->match_callbacks, &slot->match_callback);
|
2013-11-30 01:24:29 +01:00
|
|
|
|
2018-12-14 02:26:36 +01:00
|
|
|
/* Set this at last. Otherwise, some failures in above call the destroy callback but some do not. */
|
|
|
|
slot->destroy_callback = destroy_callback;
|
|
|
|
|
2018-10-15 09:06:12 +02:00
|
|
|
if (ret_slot)
|
|
|
|
*ret_slot = slot;
|
2013-11-30 01:24:29 +01:00
|
|
|
|
2018-10-15 09:06:12 +02:00
|
|
|
TAKE_PTR(slot);
|
2013-11-30 01:24:29 +01:00
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|