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 LIST_HEAD_INIT(rtnl->match_callbacks);
52 /* We guarantee that wqueue always has space for at least
54 rtnl->wqueue = new(sd_rtnl_message*, 1);
64 static bool rtnl_pid_changed(sd_rtnl *rtnl) {
67 /* We don't support people creating an rtnl connection and
68 * keeping it around over a fork(). Let's complain. */
70 return rtnl->original_pid != getpid();
73 int sd_rtnl_open(sd_rtnl **ret, uint32_t groups) {
74 _cleanup_rtnl_unref_ sd_rtnl *rtnl = NULL;
78 assert_return(ret, -EINVAL);
80 r = sd_rtnl_new(&rtnl);
84 rtnl->fd = socket(PF_NETLINK, SOCK_RAW|SOCK_CLOEXEC|SOCK_NONBLOCK, NETLINK_ROUTE);
88 rtnl->sockaddr.nl.nl_groups = groups;
90 addrlen = sizeof(rtnl->sockaddr);
92 r = bind(rtnl->fd, &rtnl->sockaddr.sa, addrlen);
96 r = getsockname(rtnl->fd, &rtnl->sockaddr.sa, &addrlen);
106 sd_rtnl *sd_rtnl_ref(sd_rtnl *rtnl) {
108 assert_se(REFCNT_INC(rtnl->n_ref) >= 2);
113 sd_rtnl *sd_rtnl_unref(sd_rtnl *rtnl) {
115 if (rtnl && REFCNT_DEC(rtnl->n_ref) <= 0) {
116 struct match_callback *f;
119 for (i = 0; i < rtnl->rqueue_size; i++)
120 sd_rtnl_message_unref(rtnl->rqueue[i]);
123 for (i = 0; i < rtnl->wqueue_size; i++)
124 sd_rtnl_message_unref(rtnl->wqueue[i]);
127 hashmap_free_free(rtnl->reply_callbacks);
128 prioq_free(rtnl->reply_callbacks_prioq);
130 while ((f = rtnl->match_callbacks)) {
131 LIST_REMOVE(match_callbacks, rtnl->match_callbacks, f);
136 close_nointr_nofail(rtnl->fd);
144 int sd_rtnl_send(sd_rtnl *nl,
145 sd_rtnl_message *message,
149 assert_return(nl, -EINVAL);
150 assert_return(!rtnl_pid_changed(nl), -ECHILD);
151 assert_return(message, -EINVAL);
153 r = rtnl_message_seal(nl, message);
157 if (nl->wqueue_size <= 0) {
159 r = socket_write_message(nl, message);
163 /* nothing was sent, so let's put it on
165 nl->wqueue[0] = sd_rtnl_message_ref(message);
171 /* append to queue */
172 if (nl->wqueue_size >= RTNL_WQUEUE_MAX)
175 q = realloc(nl->wqueue, sizeof(sd_rtnl_message*) * (nl->wqueue_size + 1));
180 q[nl->wqueue_size ++] = sd_rtnl_message_ref(message);
184 *serial = rtnl_message_get_serial(message);
189 static int dispatch_rqueue(sd_rtnl *rtnl, sd_rtnl_message **message) {
190 sd_rtnl_message *z = NULL;
196 if (rtnl->rqueue_size > 0) {
197 /* Dispatch a queued message */
199 *message = rtnl->rqueue[0];
200 rtnl->rqueue_size --;
201 memmove(rtnl->rqueue, rtnl->rqueue + 1, sizeof(sd_rtnl_message*) * rtnl->rqueue_size);
206 /* Try to read a new message */
207 r = socket_read_message(rtnl, &z);
218 static int dispatch_wqueue(sd_rtnl *rtnl) {
223 while (rtnl->wqueue_size > 0) {
224 r = socket_write_message(rtnl, rtnl->wqueue[0]);
228 /* Didn't do anything this time */
231 /* see equivalent in sd-bus.c */
232 sd_rtnl_message_unref(rtnl->wqueue[0]);
233 rtnl->wqueue_size --;
234 memmove(rtnl->wqueue, rtnl->wqueue + 1, sizeof(sd_rtnl_message*) * rtnl->wqueue_size);
243 static int process_timeout(sd_rtnl *rtnl) {
244 _cleanup_rtnl_message_unref_ sd_rtnl_message *m = NULL;
245 struct reply_callback *c;
251 c = prioq_peek(rtnl->reply_callbacks_prioq);
255 n = now(CLOCK_MONOTONIC);
259 r = rtnl_message_new_synthetic_error(-ETIMEDOUT, c->serial, &m);
263 assert_se(prioq_pop(rtnl->reply_callbacks_prioq) == c);
264 hashmap_remove(rtnl->reply_callbacks, &c->serial);
266 r = c->callback(rtnl, m, c->userdata);
269 return r < 0 ? r : 1;
272 static int process_reply(sd_rtnl *rtnl, sd_rtnl_message *m) {
273 struct reply_callback *c;
280 if (sd_rtnl_message_is_broadcast(m))
283 serial = rtnl_message_get_serial(m);
284 c = hashmap_remove(rtnl->reply_callbacks, &serial);
289 prioq_remove(rtnl->reply_callbacks_prioq, c, &c->prioq_idx);
291 r = c->callback(rtnl, m, c->userdata);
297 static int process_match(sd_rtnl *rtnl, sd_rtnl_message *m) {
298 struct match_callback *c;
305 r = sd_rtnl_message_get_type(m, &type);
309 LIST_FOREACH(match_callbacks, c, rtnl->match_callbacks) {
310 if (type == c->type) {
311 r = c->callback(rtnl, m, c->userdata);
320 static int process_running(sd_rtnl *rtnl, sd_rtnl_message **ret) {
321 _cleanup_rtnl_message_unref_ sd_rtnl_message *m = NULL;
326 r = process_timeout(rtnl);
330 r = dispatch_wqueue(rtnl);
334 r = dispatch_rqueue(rtnl, &m);
340 r = process_reply(rtnl, m);
344 r = process_match(rtnl, m);
364 int sd_rtnl_process(sd_rtnl *rtnl, sd_rtnl_message **ret) {
365 RTNL_DONT_DESTROY(rtnl);
368 assert_return(rtnl, -EINVAL);
369 assert_return(!rtnl_pid_changed(rtnl), -ECHILD);
370 assert_return(!rtnl->processing, -EBUSY);
372 rtnl->processing = true;
373 r = process_running(rtnl, ret);
374 rtnl->processing = false;
379 static usec_t calc_elapse(uint64_t usec) {
380 if (usec == (uint64_t) -1)
384 usec = RTNL_DEFAULT_TIMEOUT;
386 return now(CLOCK_MONOTONIC) + usec;
389 static int rtnl_poll(sd_rtnl *rtnl, bool need_more, uint64_t timeout_usec) {
390 struct pollfd p[1] = {};
392 usec_t m = (usec_t) -1;
397 e = sd_rtnl_get_events(rtnl);
402 /* Caller wants more data, and doesn't care about
403 * what's been read or any other timeouts. */
407 /* Caller wants to process if there is something to
408 * process, but doesn't care otherwise */
410 r = sd_rtnl_get_timeout(rtnl, &until);
415 nw = now(CLOCK_MONOTONIC);
416 m = until > nw ? until - nw : 0;
420 if (timeout_usec != (uint64_t) -1 && (m == (uint64_t) -1 || timeout_usec < m))
426 r = ppoll(p, 1, m == (uint64_t) -1 ? NULL : timespec_store(&ts, m), NULL);
430 return r > 0 ? 1 : 0;
433 int sd_rtnl_wait(sd_rtnl *nl, uint64_t timeout_usec) {
434 assert_return(nl, -EINVAL);
435 assert_return(!rtnl_pid_changed(nl), -ECHILD);
437 if (nl->rqueue_size > 0)
440 return rtnl_poll(nl, false, timeout_usec);
443 static int timeout_compare(const void *a, const void *b) {
444 const struct reply_callback *x = a, *y = b;
446 if (x->timeout != 0 && y->timeout == 0)
449 if (x->timeout == 0 && y->timeout != 0)
452 if (x->timeout < y->timeout)
455 if (x->timeout > y->timeout)
461 int sd_rtnl_call_async(sd_rtnl *nl,
463 sd_rtnl_message_handler_t callback,
467 struct reply_callback *c;
471 assert_return(nl, -EINVAL);
472 assert_return(m, -EINVAL);
473 assert_return(callback, -EINVAL);
474 assert_return(!rtnl_pid_changed(nl), -ECHILD);
476 r = hashmap_ensure_allocated(&nl->reply_callbacks, uint64_hash_func, uint64_compare_func);
480 if (usec != (uint64_t) -1) {
481 r = prioq_ensure_allocated(&nl->reply_callbacks_prioq, timeout_compare);
486 c = new0(struct reply_callback, 1);
490 c->callback = callback;
491 c->userdata = userdata;
492 c->timeout = calc_elapse(usec);
494 k = sd_rtnl_send(nl, m, &s);
502 r = hashmap_put(nl->reply_callbacks, &c->serial, c);
508 if (c->timeout != 0) {
509 r = prioq_put(nl->reply_callbacks_prioq, c, &c->prioq_idx);
512 sd_rtnl_call_async_cancel(nl, c->serial);
523 int sd_rtnl_call_async_cancel(sd_rtnl *nl, uint32_t serial) {
524 struct reply_callback *c;
527 assert_return(nl, -EINVAL);
528 assert_return(serial != 0, -EINVAL);
529 assert_return(!rtnl_pid_changed(nl), -ECHILD);
531 c = hashmap_remove(nl->reply_callbacks, &s);
536 prioq_remove(nl->reply_callbacks_prioq, c, &c->prioq_idx);
542 int sd_rtnl_call(sd_rtnl *nl,
543 sd_rtnl_message *message,
545 sd_rtnl_message **ret) {
551 assert_return(nl, -EINVAL);
552 assert_return(!rtnl_pid_changed(nl), -ECHILD);
553 assert_return(message, -EINVAL);
555 r = sd_rtnl_send(nl, message, &serial);
559 timeout = calc_elapse(usec);
563 _cleanup_rtnl_message_unref_ sd_rtnl_message *incoming = NULL;
568 if (nl->rqueue_size >= RTNL_RQUEUE_MAX)
571 /* Make sure there's room for queueing this
572 * locally, before we read the message */
574 q = realloc(nl->rqueue, (nl->rqueue_size + 1) * sizeof(sd_rtnl_message*));
582 r = socket_read_message(nl, &incoming);
586 uint32_t received_serial = rtnl_message_get_serial(incoming);
588 if (received_serial == serial) {
589 r = sd_rtnl_message_get_errno(incoming);
601 /* Room was allocated on the queue above */
602 nl->rqueue[nl->rqueue_size ++] = incoming;
606 /* Try to read more, right away */
615 n = now(CLOCK_MONOTONIC);
621 left = (uint64_t) -1;
623 r = rtnl_poll(nl, true, left);
627 r = dispatch_wqueue(nl);
633 int sd_rtnl_flush(sd_rtnl *rtnl) {
636 assert_return(rtnl, -EINVAL);
637 assert_return(!rtnl_pid_changed(rtnl), -ECHILD);
639 if (rtnl->wqueue_size <= 0)
643 r = dispatch_wqueue(rtnl);
647 if (rtnl->wqueue_size <= 0)
650 r = rtnl_poll(rtnl, false, (uint64_t) -1);
656 int sd_rtnl_get_events(sd_rtnl *rtnl) {
659 assert_return(rtnl, -EINVAL);
660 assert_return(!rtnl_pid_changed(rtnl), -ECHILD);
662 if (rtnl->rqueue_size <= 0)
664 if (rtnl->wqueue_size > 0)
670 int sd_rtnl_get_timeout(sd_rtnl *rtnl, uint64_t *timeout_usec) {
671 struct reply_callback *c;
673 assert_return(rtnl, -EINVAL);
674 assert_return(timeout_usec, -EINVAL);
675 assert_return(!rtnl_pid_changed(rtnl), -ECHILD);
677 if (rtnl->rqueue_size > 0) {
682 c = prioq_peek(rtnl->reply_callbacks_prioq);
684 *timeout_usec = (uint64_t) -1;
688 *timeout_usec = c->timeout;
693 static int io_callback(sd_event_source *s, int fd, uint32_t revents, void *userdata) {
694 sd_rtnl *rtnl = userdata;
699 r = sd_rtnl_process(rtnl, NULL);
706 static int time_callback(sd_event_source *s, uint64_t usec, void *userdata) {
707 sd_rtnl *rtnl = userdata;
712 r = sd_rtnl_process(rtnl, NULL);
719 static int prepare_callback(sd_event_source *s, void *userdata) {
720 sd_rtnl *rtnl = userdata;
727 e = sd_rtnl_get_events(rtnl);
731 r = sd_event_source_set_io_events(rtnl->io_event_source, e);
735 r = sd_rtnl_get_timeout(rtnl, &until);
741 j = sd_event_source_set_time(rtnl->time_event_source, until);
746 r = sd_event_source_set_enabled(rtnl->time_event_source, r > 0);
753 static int exit_callback(sd_event_source *event, void *userdata) {
754 sd_rtnl *rtnl = userdata;
763 int sd_rtnl_attach_event(sd_rtnl *rtnl, sd_event *event, int priority) {
766 assert_return(rtnl, -EINVAL);
767 assert_return(!rtnl->event, -EBUSY);
769 assert(!rtnl->io_event_source);
770 assert(!rtnl->time_event_source);
773 rtnl->event = sd_event_ref(event);
775 r = sd_event_default(&rtnl->event);
780 r = sd_event_add_io(rtnl->event, &rtnl->io_event_source, rtnl->fd, 0, io_callback, rtnl);
784 r = sd_event_source_set_priority(rtnl->io_event_source, priority);
788 r = sd_event_source_set_prepare(rtnl->io_event_source, prepare_callback);
792 r = sd_event_add_monotonic(rtnl->event, &rtnl->time_event_source, 0, 0, time_callback, rtnl);
796 r = sd_event_source_set_priority(rtnl->time_event_source, priority);
800 r = sd_event_add_exit(rtnl->event, &rtnl->exit_event_source, exit_callback, rtnl);
807 sd_rtnl_detach_event(rtnl);
811 int sd_rtnl_detach_event(sd_rtnl *rtnl) {
812 assert_return(rtnl, -EINVAL);
813 assert_return(rtnl->event, -ENXIO);
815 if (rtnl->io_event_source)
816 rtnl->io_event_source = sd_event_source_unref(rtnl->io_event_source);
818 if (rtnl->time_event_source)
819 rtnl->time_event_source = sd_event_source_unref(rtnl->time_event_source);
821 if (rtnl->exit_event_source)
822 rtnl->exit_event_source = sd_event_source_unref(rtnl->exit_event_source);
825 rtnl->event = sd_event_unref(rtnl->event);
830 int sd_rtnl_add_match(sd_rtnl *rtnl,
832 sd_rtnl_message_handler_t callback,
834 struct match_callback *c;
836 assert_return(rtnl, -EINVAL);
837 assert_return(callback, -EINVAL);
838 assert_return(!rtnl_pid_changed(rtnl), -ECHILD);
839 assert_return(rtnl_message_type_is_link(type) ||
840 rtnl_message_type_is_addr(type) ||
841 rtnl_message_type_is_route(type), -ENOTSUP);
843 c = new0(struct match_callback, 1);
847 c->callback = callback;
849 c->userdata = userdata;
851 LIST_PREPEND(match_callbacks, rtnl->match_callbacks, c);
856 int sd_rtnl_remove_match(sd_rtnl *rtnl,
858 sd_rtnl_message_handler_t callback,
860 struct match_callback *c;
862 assert_return(rtnl, -EINVAL);
863 assert_return(callback, -EINVAL);
864 assert_return(!rtnl_pid_changed(rtnl), -ECHILD);
866 LIST_FOREACH(match_callbacks, c, rtnl->match_callbacks)
867 if (c->callback == callback && c->type == type && c->userdata == userdata) {
868 LIST_REMOVE(match_callbacks, rtnl->match_callbacks, c);