sd-netlink.c revision 87e4c847f63ba138fa9cc5047a00d2c80b6f0d1f
/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
/***
This file is part of systemd.
Copyright 2013 Tom Gundersen <teg@jklm.no>
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>
#include "missing.h"
#include "macro.h"
#include "util.h"
#include "hashmap.h"
#include "sd-netlink.h"
#include "netlink-internal.h"
#include "netlink-util.h"
if (!rtnl)
return -ENOMEM;
/* We guarantee that the read buffer has at least space for
* a message header */
return -ENOMEM;
/* 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 */
return 0;
}
int r;
r = sd_netlink_new(&rtnl);
if (r < 0)
return r;
if (r < 0)
return -errno;
return 0;
}
/* We don't support people creating an rtnl connection and
* keeping it around over a fork(). Let's complain. */
}
int r;
r = sd_netlink_new(&rtnl);
if (r < 0)
return r;
r = socket_bind(rtnl);
if (r < 0)
return r;
return 0;
}
int r;
if (fd < 0)
return fd;
if (r < 0)
return r;
fd = -1;
return 0;
}
}
if (rtnl)
return rtnl;
}
if (!rtnl)
return NULL;
struct match_callback *f;
unsigned i;
for (i = 0; i < rtnl->rqueue_size; i++)
for (i = 0; i < rtnl->rqueue_partial_size; i++)
while ((f = rtnl->match_callbacks)) {
free(f);
}
}
return NULL;
}
assert(m);
/* don't use seq == 0, as that is used for broadcasts, so we
would get confused by replies to such messages */
return;
}
int r;
if (r < 0)
return r;
if (serial)
return 1;
}
return -ENOBUFS;
}
return -ENOMEM;
return 0;
}
return -ENOBUFS;
}
return -ENOMEM;
return 0;
}
int r;
if (rtnl->rqueue_size <= 0) {
/* Try to read a new message */
r = socket_read_message(rtnl);
if (r <= 0)
return r;
}
/* Dispatch a queued message */
rtnl->rqueue_size --;
return 1;
}
struct reply_callback *c;
usec_t n;
int r;
if (!c)
return 0;
n = now(CLOCK_MONOTONIC);
if (c->timeout > n)
return 0;
if (r < 0)
return r;
if (r < 0)
log_debug_errno(r, "sd-netlink: timedout callback failed: %m");
free(c);
return 1;
}
int r;
assert(m);
if (!c)
return 0;
if (c->timeout != 0)
r = sd_netlink_message_get_type(m, &type);
if (r < 0)
return 0;
if (type == NLMSG_DONE)
m = NULL;
if (r < 0)
log_debug_errno(r, "sd-netlink: callback failed: %m");
return 1;
}
struct match_callback *c;
int r;
assert(m);
r = sd_netlink_message_get_type(m, &type);
if (r < 0)
return r;
if (r != 0) {
if (r < 0)
log_debug_errno(r, "sd-netlink: match callback failed: %m");
break;
}
}
}
return 1;
}
int r;
r = process_timeout(rtnl);
if (r != 0)
goto null_message;
r = dispatch_rqueue(rtnl, &m);
if (r < 0)
return r;
if (!m)
goto null_message;
if (sd_netlink_message_is_broadcast(m)) {
r = process_match(rtnl, m);
if (r != 0)
goto null_message;
} else {
r = process_reply(rtnl, m);
if (r != 0)
goto null_message;
}
if (ret) {
*ret = m;
m = NULL;
return 1;
}
return 1;
if (r >= 0 && ret)
return r;
}
int r;
rtnl->processing = true;
rtnl->processing = false;
return r;
}
return 0;
if (usec == 0)
}
struct pollfd p[1] = {};
usec_t m = USEC_INFINITY;
int r, e;
e = sd_netlink_get_events(rtnl);
if (e < 0)
return e;
if (need_more)
/* Caller wants more data, and doesn't care about
* what's been read or any other timeouts. */
e |= POLLIN;
else {
/* Caller wants to process if there is something to
* process, but doesn't care otherwise */
if (r < 0)
return r;
if (r > 0) {
}
}
m = timeout_usec;
p[0].events = e;
if (r < 0)
return -errno;
return r > 0 ? 1 : 0;
}
if (nl->rqueue_size > 0)
return 0;
}
static int timeout_compare(const void *a, const void *b) {
const struct reply_callback *x = a, *y = b;
return -1;
return 1;
return -1;
return 1;
return 0;
}
void *userdata,
struct reply_callback *c;
uint32_t s;
int r, k;
assert_return(m, -EINVAL);
if (r < 0)
return r;
if (r < 0)
return r;
}
if (!c)
return -ENOMEM;
k = sd_netlink_send(nl, m, &s);
if (k < 0) {
free(c);
return k;
}
c->serial = s;
if (r < 0) {
free(c);
return r;
}
if (c->timeout != 0) {
if (r > 0) {
c->timeout = 0;
return r;
}
}
if (serial)
*serial = s;
return k;
}
struct reply_callback *c;
if (!c)
return 0;
if (c->timeout != 0)
free(c);
return 1;
}
sd_netlink_message **ret) {
int r;
if (r < 0)
return r;
for (;;) {
unsigned i;
for (i = 0; i < rtnl->rqueue_size; i++) {
if (received_serial == serial) {
/* found a match, remove from rqueue and return it */
rtnl->rqueue_size--;
if (r < 0)
return r;
if (r < 0)
return r;
if (type == NLMSG_DONE) {
return 0;
}
if (ret) {
}
return 1;
}
}
r = socket_read_message(rtnl);
if (r < 0)
return r;
if (r > 0)
/* received message, so try to process straight away */
continue;
if (timeout > 0) {
usec_t n;
n = now(CLOCK_MONOTONIC);
if (n >= timeout)
return -ETIMEDOUT;
} else
if (r < 0)
return r;
else if (r == 0)
return -ETIMEDOUT;
}
}
if (rtnl->rqueue_size == 0)
return POLLIN;
else
return 0;
}
struct reply_callback *c;
if (rtnl->rqueue_size > 0) {
*timeout_usec = 0;
return 1;
}
if (!c) {
return 0;
}
*timeout_usec = c->timeout;
return 1;
}
int r;
if (r < 0)
return r;
return 1;
}
int r;
if (r < 0)
return r;
return 1;
}
int r, e;
assert(s);
e = sd_netlink_get_events(rtnl);
if (e < 0)
return e;
if (r < 0)
return r;
if (r < 0)
return r;
if (r > 0) {
int j;
if (j < 0)
return j;
}
if (r < 0)
return r;
return 1;
}
int r;
if (event)
else {
if (r < 0)
return r;
}
if (r < 0)
goto fail;
if (r < 0)
goto fail;
if (r < 0)
goto fail;
if (r < 0)
goto fail;
r = sd_event_add_time(rtnl->event, &rtnl->time_event_source, CLOCK_MONOTONIC, 0, 0, time_callback, rtnl);
if (r < 0)
goto fail;
if (r < 0)
goto fail;
if (r < 0)
goto fail;
return 0;
fail:
return r;
}
return 0;
}
void *userdata) {
int r;
if (!c)
return -ENOMEM;
switch (type) {
case RTM_NEWLINK:
case RTM_DELLINK:
if (r < 0)
return r;
break;
case RTM_NEWADDR:
case RTM_DELADDR:
if (r < 0)
return r;
if (r < 0)
return r;
break;
case RTM_NEWROUTE:
case RTM_DELROUTE:
if (r < 0)
return r;
if (r < 0)
return r;
break;
default:
return -EOPNOTSUPP;
}
c = NULL;
return 0;
}
void *userdata) {
struct match_callback *c;
/* we should unsubscribe from the broadcast groups at this point, but it is not so
trivial for a few reasons: the refcounting is a bit of a mess and not obvious
how it will look like after we add genetlink support, and it is also not possible
to query what broadcast groups were subscribed to when we inherit the socket to get
the initial refcount. The latter could indeed be done for the first 32 broadcast
groups (which incidentally is all we currently support in .socket units anyway),
but we better not rely on only ever using 32 groups. */
free(c);
return 1;
}
return 0;
}