1 /*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
4 This file is part of systemd.
6 Copyright 2013 Tom Gundersen <teg@jklm.no>
8 systemd is free software; you can redistribute it and/or modify it
9 under the terms of the GNU Lesser General Public License as published by
10 the Free Software Foundation; either version 2.1 of the License, or
11 (at your option) any later version.
13 systemd is distributed in the hope that it will be useful, but
14 WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 Lesser General Public License for more details.
18 You should have received a copy of the GNU Lesser General Public License
19 along with systemd; If not, see <http://www.gnu.org/licenses/>.
22 #include <sys/socket.h>
30 #include "rtnl-internal.h"
31 #include "rtnl-util.h"
33 static int sd_rtnl_new(sd_rtnl **ret) {
36 assert_return(ret, -EINVAL);
38 rtnl = new0(sd_rtnl, 1);
42 rtnl->n_ref = REFCNT_INIT;
46 rtnl->sockaddr.nl.nl_family = AF_NETLINK;
48 rtnl->original_pid = getpid();
50 /* We guarantee that wqueue always has space for at least
52 rtnl->wqueue = new(sd_rtnl_message*, 1);
62 static bool rtnl_pid_changed(sd_rtnl *rtnl) {
65 /* We don't support people creating an rtnl connection and
66 * keeping it around over a fork(). Let's complain. */
68 return rtnl->original_pid != getpid();
71 int sd_rtnl_open(uint32_t groups, sd_rtnl **ret) {
72 _cleanup_sd_rtnl_unref_ sd_rtnl *rtnl = NULL;
76 r = sd_rtnl_new(&rtnl);
80 rtnl->fd = socket(PF_NETLINK, SOCK_RAW|SOCK_CLOEXEC|SOCK_NONBLOCK, NETLINK_ROUTE);
84 rtnl->sockaddr.nl.nl_groups = groups;
86 addrlen = sizeof(rtnl->sockaddr);
88 r = bind(rtnl->fd, &rtnl->sockaddr.sa, addrlen);
92 r = getsockname(rtnl->fd, &rtnl->sockaddr.sa, &addrlen);
102 sd_rtnl *sd_rtnl_ref(sd_rtnl *rtnl) {
104 assert_se(REFCNT_INC(rtnl->n_ref) >= 2);
109 sd_rtnl *sd_rtnl_unref(sd_rtnl *rtnl) {
111 if (rtnl && REFCNT_DEC(rtnl->n_ref) <= 0) {
114 for (i = 0; i < rtnl->rqueue_size; i++)
115 sd_rtnl_message_unref(rtnl->rqueue[i]);
118 for (i = 0; i < rtnl->wqueue_size; i++)
119 sd_rtnl_message_unref(rtnl->wqueue[i]);
122 hashmap_free_free(rtnl->reply_callbacks);
123 prioq_free(rtnl->reply_callbacks_prioq);
126 close_nointr_nofail(rtnl->fd);
134 int sd_rtnl_send(sd_rtnl *nl,
135 sd_rtnl_message *message,
139 assert_return(nl, -EINVAL);
140 assert_return(!rtnl_pid_changed(nl), -ECHILD);
141 assert_return(message, -EINVAL);
143 r = message_seal(nl, message);
147 if (nl->wqueue_size <= 0) {
149 r = socket_write_message(nl, message);
153 /* nothing was sent, so let's put it on
155 nl->wqueue[0] = sd_rtnl_message_ref(message);
161 /* append to queue */
162 if (nl->wqueue_size >= RTNL_WQUEUE_MAX)
165 q = realloc(nl->wqueue, sizeof(sd_rtnl_message*) * (nl->wqueue_size + 1));
170 q[nl->wqueue_size ++] = sd_rtnl_message_ref(message);
174 *serial = message_get_serial(message);
179 static int dispatch_rqueue(sd_rtnl *rtnl, sd_rtnl_message **message) {
180 sd_rtnl_message *z = NULL;
186 if (rtnl->rqueue_size > 0) {
187 /* Dispatch a queued message */
189 *message = rtnl->rqueue[0];
190 rtnl->rqueue_size --;
191 memmove(rtnl->rqueue, rtnl->rqueue + 1, sizeof(sd_rtnl_message*) * rtnl->rqueue_size);
196 /* Try to read a new message */
197 r = socket_read_message(rtnl, &z);
208 static int dispatch_wqueue(sd_rtnl *rtnl) {
213 while (rtnl->wqueue_size > 0) {
214 r = socket_write_message(rtnl, rtnl->wqueue[0]);
218 /* Didn't do anything this time */
221 /* see equivalent in sd-bus.c */
222 sd_rtnl_message_unref(rtnl->wqueue[0]);
223 rtnl->wqueue_size --;
224 memmove(rtnl->wqueue, rtnl->wqueue + 1, sizeof(sd_rtnl_message*) * rtnl->wqueue_size);
233 static int process_timeout(sd_rtnl *rtnl) {
234 _cleanup_sd_rtnl_message_unref_ sd_rtnl_message *m = NULL;
235 struct reply_callback *c;
241 c = prioq_peek(rtnl->reply_callbacks_prioq);
245 n = now(CLOCK_MONOTONIC);
249 r = message_new_synthetic_error(-ETIMEDOUT, c->serial, &m);
253 assert_se(prioq_pop(rtnl->reply_callbacks_prioq) == c);
254 hashmap_remove(rtnl->reply_callbacks, &c->serial);
256 r = c->callback(rtnl, m, c->userdata);
259 return r < 0 ? r : 1;
262 static int process_reply(sd_rtnl *rtnl, sd_rtnl_message *m) {
263 struct reply_callback *c;
270 serial = message_get_serial(m);
271 c = hashmap_remove(rtnl->reply_callbacks, &serial);
276 prioq_remove(rtnl->reply_callbacks_prioq, c, &c->prioq_idx);
278 r = c->callback(rtnl, m, c->userdata);
284 static int process_running(sd_rtnl *rtnl, sd_rtnl_message **ret) {
285 _cleanup_sd_rtnl_message_unref_ sd_rtnl_message *m = NULL;
288 r = process_timeout(rtnl);
292 r = dispatch_wqueue(rtnl);
296 r = dispatch_rqueue(rtnl, &m);
302 r = process_reply(rtnl, m);
322 int sd_rtnl_process(sd_rtnl *rtnl, sd_rtnl_message **ret) {
323 RTNL_DONT_DESTROY(rtnl);
326 assert_return(rtnl, -EINVAL);
327 assert_return(!rtnl_pid_changed(rtnl), -ECHILD);
328 assert_return(!rtnl->processing, -EBUSY);
330 rtnl->processing = true;
331 r = process_running(rtnl, ret);
332 rtnl->processing = false;
337 static usec_t calc_elapse(uint64_t usec) {
338 if (usec == (uint64_t) -1)
342 usec = RTNL_DEFAULT_TIMEOUT;
344 return now(CLOCK_MONOTONIC) + usec;
347 static int rtnl_poll(sd_rtnl *nl, uint64_t timeout_usec) {
348 struct pollfd p[1] = {};
355 p[0].events = POLLIN;
357 r = ppoll(p, 1, timeout_usec == (uint64_t) -1 ? NULL :
358 timespec_store(&ts, timeout_usec), NULL);
362 return r > 0 ? 1 : 0;
365 int sd_rtnl_wait(sd_rtnl *nl, uint64_t timeout_usec) {
366 assert_return(nl, -EINVAL);
367 assert_return(!rtnl_pid_changed(nl), -ECHILD);
369 if (nl->rqueue_size > 0)
372 return rtnl_poll(nl, timeout_usec);
375 static int timeout_compare(const void *a, const void *b) {
376 const struct reply_callback *x = a, *y = b;
378 if (x->timeout != 0 && y->timeout == 0)
381 if (x->timeout == 0 && y->timeout != 0)
384 if (x->timeout < y->timeout)
387 if (x->timeout > y->timeout)
393 int sd_rtnl_call_async(sd_rtnl *nl,
395 sd_rtnl_message_handler_t callback,
399 struct reply_callback *c;
403 assert_return(nl, -EINVAL);
404 assert_return(m, -EINVAL);
405 assert_return(callback, -EINVAL);
406 assert_return(!rtnl_pid_changed(nl), -ECHILD);
408 r = hashmap_ensure_allocated(&nl->reply_callbacks, uint64_hash_func, uint64_compare_func);
412 if (usec != (uint64_t) -1) {
413 r = prioq_ensure_allocated(&nl->reply_callbacks_prioq, timeout_compare);
418 c = new0(struct reply_callback, 1);
422 c->callback = callback;
423 c->userdata = userdata;
424 c->timeout = calc_elapse(usec);
426 k = sd_rtnl_send(nl, m, &s);
434 r = hashmap_put(nl->reply_callbacks, &c->serial, c);
440 if (c->timeout != 0) {
441 r = prioq_put(nl->reply_callbacks_prioq, c, &c->prioq_idx);
444 sd_rtnl_call_async_cancel(nl, c->serial);
455 int sd_rtnl_call_async_cancel(sd_rtnl *nl, uint32_t serial) {
456 struct reply_callback *c;
459 assert_return(nl, -EINVAL);
460 assert_return(serial != 0, -EINVAL);
461 assert_return(!rtnl_pid_changed(nl), -ECHILD);
463 c = hashmap_remove(nl->reply_callbacks, &s);
468 prioq_remove(nl->reply_callbacks_prioq, c, &c->prioq_idx);
474 int sd_rtnl_call(sd_rtnl *nl,
475 sd_rtnl_message *message,
477 sd_rtnl_message **ret) {
483 assert_return(nl, -EINVAL);
484 assert_return(!rtnl_pid_changed(nl), -ECHILD);
485 assert_return(message, -EINVAL);
487 r = sd_rtnl_send(nl, message, &serial);
491 timeout = calc_elapse(usec);
495 _cleanup_sd_rtnl_message_unref_ sd_rtnl_message *incoming = NULL;
500 if (nl->rqueue_size >= RTNL_RQUEUE_MAX)
503 /* Make sure there's room for queueing this
504 * locally, before we read the message */
506 q = realloc(nl->rqueue, (nl->rqueue_size + 1) * sizeof(sd_rtnl_message*));
514 r = socket_read_message(nl, &incoming);
518 uint32_t received_serial = message_get_serial(incoming);
520 if (received_serial == serial) {
521 r = sd_rtnl_message_get_errno(incoming);
533 /* Room was allocated on the queue above */
534 nl->rqueue[nl->rqueue_size ++] = incoming;
538 /* Try to read more, right away */
547 n = now(CLOCK_MONOTONIC);
553 left = (uint64_t) -1;
555 r = rtnl_poll(nl, left);
559 r = dispatch_wqueue(nl);