sd-rtnl.c revision 31710be527104abad7541b122ee10c4560bd14d2
/*-*- 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-rtnl.h"
#include "rtnl-internal.h"
#include "rtnl-util.h"
if (!rtnl)
return -ENOMEM;
/* We guarantee that wqueue always has space for at least
* one entry */
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_rtnl_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, one = 1;
r = sd_rtnl_new(&rtnl);
if (r < 0)
return r;
if (r < 0)
return -errno;
if (r < 0)
return -errno;
/* ignore EINVAL to allow opening an already bound socket */
return -errno;
if (r < 0)
return -errno;
return 0;
}
int r;
if (fd < 0)
return -errno;
if (r < 0)
return r;
fd = -1;
return 0;
}
int r;
if (r < 0)
return -errno;
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++)
for (i = 0; i < rtnl->wqueue_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 (nl->wqueue_size <= 0) {
/* send directly */
if (r < 0)
return r;
else if (r == 0) {
/* nothing was sent, so let's put it on
* the queue */
}
} else {
/* append to queue */
return -ENOBUFS;
}
return -ENOMEM;
}
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;
}
int r, ret = 0;
while (rtnl->wqueue_size > 0) {
if (r < 0)
return r;
else if (r == 0)
/* Didn't do anything this time */
return ret;
else {
/* see equivalent in sd-bus.c */
rtnl->wqueue_size --;
ret = 1;
}
}
return ret;
}
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-rtnl: timedout callback failed: %m");
free(c);
return 1;
}
int r;
assert(m);
if (!c)
return 0;
if (c->timeout != 0)
r = sd_rtnl_message_get_type(m, &type);
if (r < 0)
return 0;
if (type == NLMSG_DONE)
m = NULL;
if (r < 0)
log_debug_errno(r, "sd-rtnl: callback failed: %m");
return 1;
}
struct match_callback *c;
int r;
assert(m);
r = sd_rtnl_message_get_type(m, &type);
if (r < 0)
return r;
if (r != 0) {
if (r < 0)
log_debug_errno(r, "sd-rtnl: match callback failed: %m");
break;
}
}
}
return 1;
}
int r;
r = process_timeout(rtnl);
if (r != 0)
goto null_message;
r = dispatch_wqueue(rtnl);
if (r != 0)
goto null_message;
r = dispatch_rqueue(rtnl, &m);
if (r < 0)
return r;
if (!m)
goto null_message;
if (sd_rtnl_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_rtnl_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;
}
sd_rtnl_message *m,
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_rtnl_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_rtnl_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;
r = dispatch_wqueue(rtnl);
if (r < 0)
return r;
}
}
int r;
if (rtnl->wqueue_size <= 0)
return 0;
for (;;) {
r = dispatch_wqueue(rtnl);
if (r < 0)
return r;
if (rtnl->wqueue_size <= 0)
return 0;
if (r < 0)
return r;
}
}
int flags = 0;
if (rtnl->rqueue_size <= 0)
if (rtnl->wqueue_size > 0)
return flags;
}
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_rtnl_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;
}
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;
if (r < 0)
goto fail;
if (r < 0)
goto fail;
return 0;
fail:
return r;
}
if (rtnl->io_event_source)
if (rtnl->time_event_source)
if (rtnl->exit_event_source)
return 0;
}
void *userdata) {
int r;
if (!c)
return -ENOMEM;
switch (type) {
case RTM_NEWLINK:
case RTM_SETLINK:
case RTM_GETLINK:
case RTM_DELLINK:
if (r < 0)
return r;
break;
case RTM_NEWADDR:
case RTM_GETADDR:
case RTM_DELADDR:
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;
}