2017-11-18 17:09:20 +01:00
|
|
|
/* SPDX-License-Identifier: LGPL-2.1+ */
|
2013-10-21 20:12:52 +02:00
|
|
|
/***
|
|
|
|
This file is part of systemd.
|
|
|
|
|
|
|
|
Copyright 2013 Tom Gundersen <teg@jklm.no>
|
|
|
|
|
|
|
|
systemd is free software; you can redistribute it and/or modify it
|
|
|
|
under the terms of the GNU Lesser General Public License as published by
|
|
|
|
the Free Software Foundation; either version 2.1 of the License, or
|
|
|
|
(at your option) any later version.
|
|
|
|
|
|
|
|
systemd is distributed in the hope that it will be useful, but
|
|
|
|
WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
|
|
Lesser General Public License for more details.
|
|
|
|
|
|
|
|
You should have received a copy of the GNU Lesser General Public License
|
|
|
|
along with systemd; If not, see <http://www.gnu.org/licenses/>.
|
|
|
|
***/
|
|
|
|
|
|
|
|
#include <poll.h>
|
2015-10-24 22:58:24 +02:00
|
|
|
#include <sys/socket.h>
|
2013-10-21 20:12:52 +02:00
|
|
|
|
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"
|
|
|
|
#include "macro.h"
|
|
|
|
#include "missing.h"
|
2015-06-12 16:31:33 +02:00
|
|
|
#include "netlink-internal.h"
|
|
|
|
#include "netlink-util.h"
|
2015-10-26 01:09:02 +01:00
|
|
|
#include "socket-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);
|
|
|
|
|
2015-06-12 16:31:33 +02:00
|
|
|
rtnl = new0(sd_netlink, 1);
|
2013-10-21 20:12:52 +02:00
|
|
|
if (!rtnl)
|
|
|
|
return -ENOMEM;
|
|
|
|
|
|
|
|
rtnl->n_ref = REFCNT_INIT;
|
|
|
|
rtnl->fd = -1;
|
|
|
|
rtnl->sockaddr.nl.nl_family = AF_NETLINK;
|
2017-07-20 16:19:18 +02:00
|
|
|
rtnl->original_pid = getpid_cached();
|
2017-12-18 15:17:06 +01:00
|
|
|
rtnl->protocol = -1;
|
2013-10-29 17:38:31 +01:00
|
|
|
|
2013-11-30 01:24:29 +01:00
|
|
|
LIST_HEAD_INIT(rtnl->match_callbacks);
|
|
|
|
|
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;
|
|
|
|
|
2015-03-12 19:14:58 +01: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 */
|
|
|
|
rtnl->serial = 1;
|
|
|
|
|
2013-10-21 20:12:52 +02:00
|
|
|
*ret = rtnl;
|
2014-03-31 14:43:34 +02:00
|
|
|
rtnl = NULL;
|
|
|
|
|
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;
|
|
|
|
|
|
|
|
*ret = rtnl;
|
|
|
|
rtnl = NULL;
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2015-06-12 16:31:33 +02:00
|
|
|
static bool rtnl_pid_changed(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
|
|
|
|
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
|
|
|
|
2013-10-21 20:12:52 +02:00
|
|
|
*ret = rtnl;
|
2013-10-29 15:56:28 +01:00
|
|
|
rtnl = NULL;
|
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);
|
|
|
|
}
|
|
|
|
|
2015-06-12 16:31:33 +02:00
|
|
|
sd_netlink *sd_netlink_ref(sd_netlink *rtnl) {
|
2014-03-23 15:33:24 +01:00
|
|
|
assert_return(rtnl, NULL);
|
|
|
|
assert_return(!rtnl_pid_changed(rtnl), NULL);
|
|
|
|
|
2013-10-21 20:12:52 +02:00
|
|
|
if (rtnl)
|
|
|
|
assert_se(REFCNT_INC(rtnl->n_ref) >= 2);
|
|
|
|
|
|
|
|
return rtnl;
|
|
|
|
}
|
|
|
|
|
2015-06-12 16:31:33 +02:00
|
|
|
sd_netlink *sd_netlink_unref(sd_netlink *rtnl) {
|
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
|
|
|
if (!rtnl)
|
|
|
|
return NULL;
|
|
|
|
|
2014-03-23 15:33:24 +01:00
|
|
|
assert_return(!rtnl_pid_changed(rtnl), NULL);
|
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
|
|
|
|
2015-01-13 23:03:11 +01:00
|
|
|
if (REFCNT_DEC(rtnl->n_ref) == 0) {
|
2013-11-30 01:24:29 +01:00
|
|
|
struct match_callback *f;
|
2013-11-12 22:37:51 +01:00
|
|
|
unsigned i;
|
|
|
|
|
2014-03-23 15:33:24 +01:00
|
|
|
for (i = 0; i < rtnl->rqueue_size; i++)
|
2015-06-12 16:31:33 +02:00
|
|
|
sd_netlink_message_unref(rtnl->rqueue[i]);
|
2014-03-23 15:33:24 +01:00
|
|
|
free(rtnl->rqueue);
|
2013-11-30 01:24:29 +01:00
|
|
|
|
2014-04-17 21:32:25 +02:00
|
|
|
for (i = 0; i < rtnl->rqueue_partial_size; i++)
|
2015-06-12 16:31:33 +02:00
|
|
|
sd_netlink_message_unref(rtnl->rqueue_partial[i]);
|
2014-04-17 21:32:25 +02:00
|
|
|
free(rtnl->rqueue_partial);
|
|
|
|
|
2014-04-14 17:20:51 +02:00
|
|
|
free(rtnl->rbuffer);
|
|
|
|
|
2014-03-23 15:33:24 +01:00
|
|
|
hashmap_free_free(rtnl->reply_callbacks);
|
|
|
|
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
|
|
|
|
2014-03-23 15:33:24 +01: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
|
|
|
|
2014-03-23 15:33:24 +01:00
|
|
|
while ((f = rtnl->match_callbacks)) {
|
2015-10-15 17:59:10 +02:00
|
|
|
sd_netlink_remove_match(rtnl, f->type, f->callback, f->userdata);
|
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
|
|
|
}
|
2013-10-21 20:12:52 +02:00
|
|
|
|
2015-10-15 17:59:10 +02:00
|
|
|
hashmap_free(rtnl->broadcast_group_refs);
|
|
|
|
|
2014-03-23 15:33:24 +01:00
|
|
|
safe_close(rtnl->fd);
|
|
|
|
free(rtnl);
|
|
|
|
}
|
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
|
|
|
|
2013-10-21 20:12:52 +02:00
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
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,
|
|
|
|
sd_netlink_message *message,
|
2013-11-12 22:37:51 +01:00
|
|
|
uint32_t *serial) {
|
|
|
|
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);
|
|
|
|
|
2014-04-20 07:51:28 +02:00
|
|
|
if (rtnl->rqueue_size >= RTNL_RQUEUE_MAX) {
|
|
|
|
log_debug("rtnl: exhausted the read queue size (%d)", RTNL_RQUEUE_MAX);
|
2014-04-10 19:40:48 +02:00
|
|
|
return -ENOBUFS;
|
2014-04-20 07:51:28 +02:00
|
|
|
}
|
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);
|
|
|
|
|
2014-04-20 07:51:28 +02:00
|
|
|
if (rtnl->rqueue_partial_size >= RTNL_RQUEUE_MAX) {
|
|
|
|
log_debug("rtnl: exhausted the partial read queue size (%d)", RTNL_RQUEUE_MAX);
|
2014-04-17 21:32:25 +02:00
|
|
|
return -ENOBUFS;
|
2014-04-20 07:51:28 +02:00
|
|
|
}
|
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;
|
|
|
|
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);
|
|
|
|
hashmap_remove(rtnl->reply_callbacks, &c->serial);
|
|
|
|
|
|
|
|
r = c->callback(rtnl, m, c->userdata);
|
2015-01-29 07:26:58 +01:00
|
|
|
if (r < 0)
|
2015-06-12 16:31:33 +02:00
|
|
|
log_debug_errno(r, "sd-netlink: timedout callback failed: %m");
|
2015-01-29 07:26:58 +01:00
|
|
|
|
2013-11-13 23:18:20 +01:00
|
|
|
free(c);
|
|
|
|
|
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) {
|
2015-03-18 13:06:19 +01:00
|
|
|
_cleanup_free_ struct reply_callback *c = NULL;
|
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;
|
|
|
|
|
|
|
|
if (c->timeout != 0)
|
|
|
|
prioq_remove(rtnl->reply_callbacks_prioq, c, &c->prioq_idx);
|
|
|
|
|
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)
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
if (type == NLMSG_DONE)
|
|
|
|
m = NULL;
|
|
|
|
|
2013-11-13 23:18:20 +01:00
|
|
|
r = c->callback(rtnl, m, c->userdata);
|
2015-01-29 07:26:58 +01:00
|
|
|
if (r < 0)
|
2015-06-12 16:31:33 +02:00
|
|
|
log_debug_errno(r, "sd-netlink: callback failed: %m");
|
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;
|
|
|
|
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) {
|
2013-11-30 01:24:29 +01:00
|
|
|
r = c->callback(rtnl, m, c->userdata);
|
2015-01-29 07:26:58 +01:00
|
|
|
if (r != 0) {
|
|
|
|
if (r < 0)
|
2015-06-12 16:31:33 +02:00
|
|
|
log_debug_errno(r, "sd-netlink: match callback failed: %m");
|
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) {
|
|
|
|
*ret = m;
|
|
|
|
m = NULL;
|
|
|
|
|
|
|
|
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) {
|
2013-11-12 22:37:51 +01:00
|
|
|
struct pollfd p[1] = {};
|
|
|
|
struct timespec ts;
|
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
|
|
|
|
2013-11-14 19:23:39 +01:00
|
|
|
if (timeout_usec != (uint64_t) -1 && (m == (uint64_t) -1 || timeout_usec < m))
|
|
|
|
m = timeout_usec;
|
|
|
|
|
|
|
|
p[0].fd = rtnl->fd;
|
|
|
|
p[0].events = e;
|
|
|
|
|
|
|
|
r = ppoll(p, 1, m == (uint64_t) -1 ? NULL : timespec_store(&ts, m), NULL);
|
2013-11-12 22:37:51 +01:00
|
|
|
if (r < 0)
|
|
|
|
return -errno;
|
|
|
|
|
|
|
|
return r > 0 ? 1 : 0;
|
|
|
|
}
|
|
|
|
|
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;
|
|
|
|
|
|
|
|
if (x->timeout < y->timeout)
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
if (x->timeout > y->timeout)
|
|
|
|
return 1;
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2015-06-12 16:31:33 +02:00
|
|
|
int sd_netlink_call_async(sd_netlink *nl,
|
|
|
|
sd_netlink_message *m,
|
|
|
|
sd_netlink_message_handler_t callback,
|
2013-11-13 23:18:20 +01:00
|
|
|
void *userdata,
|
|
|
|
uint64_t usec,
|
|
|
|
uint32_t *serial) {
|
|
|
|
struct reply_callback *c;
|
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
|
|
|
c = new0(struct reply_callback, 1);
|
|
|
|
if (!c)
|
|
|
|
return -ENOMEM;
|
|
|
|
|
|
|
|
c->callback = callback;
|
|
|
|
c->userdata = userdata;
|
|
|
|
c->timeout = calc_elapse(usec);
|
|
|
|
|
2015-06-12 16:31:33 +02:00
|
|
|
k = sd_netlink_send(nl, m, &s);
|
2013-11-13 23:18:20 +01:00
|
|
|
if (k < 0) {
|
|
|
|
free(c);
|
|
|
|
return k;
|
|
|
|
}
|
|
|
|
|
|
|
|
c->serial = s;
|
|
|
|
|
|
|
|
r = hashmap_put(nl->reply_callbacks, &c->serial, c);
|
|
|
|
if (r < 0) {
|
|
|
|
free(c);
|
|
|
|
return r;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (c->timeout != 0) {
|
|
|
|
r = prioq_put(nl->reply_callbacks_prioq, c, &c->prioq_idx);
|
|
|
|
if (r > 0) {
|
|
|
|
c->timeout = 0;
|
2015-06-12 16:31:33 +02:00
|
|
|
sd_netlink_call_async_cancel(nl, c->serial);
|
2013-11-13 23:18:20 +01:00
|
|
|
return r;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (serial)
|
|
|
|
*serial = s;
|
|
|
|
|
|
|
|
return k;
|
|
|
|
}
|
|
|
|
|
2015-06-12 16:31:33 +02:00
|
|
|
int sd_netlink_call_async_cancel(sd_netlink *nl, uint32_t serial) {
|
2013-11-13 23:18:20 +01:00
|
|
|
struct reply_callback *c;
|
|
|
|
uint64_t s = serial;
|
|
|
|
|
|
|
|
assert_return(nl, -EINVAL);
|
|
|
|
assert_return(serial != 0, -EINVAL);
|
|
|
|
assert_return(!rtnl_pid_changed(nl), -ECHILD);
|
|
|
|
|
|
|
|
c = hashmap_remove(nl->reply_callbacks, &s);
|
|
|
|
if (!c)
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
if (c->timeout != 0)
|
|
|
|
prioq_remove(nl->reply_callbacks_prioq, c, &c->prioq_idx);
|
|
|
|
|
|
|
|
free(c);
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
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
|
|
|
|
2013-10-29 15:56:28 +01:00
|
|
|
if (ret) {
|
2013-11-12 22:37:51 +01:00
|
|
|
*ret = incoming;
|
2015-03-18 13:06:19 +01:00
|
|
|
incoming = NULL;
|
|
|
|
}
|
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
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-06-12 16:31:33 +02:00
|
|
|
int sd_netlink_get_events(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
|
|
|
}
|
|
|
|
|
2015-06-12 16:31:33 +02:00
|
|
|
int sd_netlink_get_timeout(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
|
|
|
|
2015-06-12 16:31:33 +02:00
|
|
|
int sd_netlink_add_match(sd_netlink *rtnl,
|
2013-12-06 15:20:36 +01:00
|
|
|
uint16_t type,
|
2015-06-12 16:31:33 +02:00
|
|
|
sd_netlink_message_handler_t callback,
|
2013-11-30 01:24:29 +01:00
|
|
|
void *userdata) {
|
2015-06-11 15:55:37 +02:00
|
|
|
_cleanup_free_ struct match_callback *c = NULL;
|
|
|
|
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);
|
|
|
|
|
|
|
|
c = new0(struct match_callback, 1);
|
|
|
|
if (!c)
|
|
|
|
return -ENOMEM;
|
|
|
|
|
|
|
|
c->callback = callback;
|
2013-12-06 15:20:36 +01:00
|
|
|
c->type = type;
|
2013-11-30 01:24:29 +01:00
|
|
|
c->userdata = userdata;
|
|
|
|
|
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;
|
|
|
|
|
|
|
|
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;
|
2015-06-11 15:55:37 +02:00
|
|
|
default:
|
|
|
|
return -EOPNOTSUPP;
|
|
|
|
}
|
|
|
|
|
2013-11-30 01:24:29 +01:00
|
|
|
LIST_PREPEND(match_callbacks, rtnl->match_callbacks, c);
|
|
|
|
|
2015-06-11 15:55:37 +02:00
|
|
|
c = NULL;
|
|
|
|
|
2013-11-30 01:24:29 +01:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2015-06-12 16:31:33 +02:00
|
|
|
int sd_netlink_remove_match(sd_netlink *rtnl,
|
2013-12-06 15:20:36 +01:00
|
|
|
uint16_t type,
|
2015-06-12 16:31:33 +02:00
|
|
|
sd_netlink_message_handler_t callback,
|
2013-11-30 01:24:29 +01:00
|
|
|
void *userdata) {
|
|
|
|
struct match_callback *c;
|
2015-10-15 17:59:10 +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);
|
|
|
|
|
|
|
|
LIST_FOREACH(match_callbacks, c, rtnl->match_callbacks)
|
2013-12-06 15:20:36 +01:00
|
|
|
if (c->callback == callback && c->type == type && c->userdata == userdata) {
|
2013-11-30 01:24:29 +01:00
|
|
|
LIST_REMOVE(match_callbacks, rtnl->match_callbacks, c);
|
|
|
|
free(c);
|
|
|
|
|
2015-10-15 17:59:10 +02:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2013-11-30 01:24:29 +01:00
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|