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) {
120 * If our ref-cnt is exactly the number of internally queued messages
121 * plus the ref-cnt to be dropped, then we know there's no external
122 * reference to us. Hence, we look through all queued messages and if
123 * they also have no external references, we're about to drop the last
124 * ref. Flush the queues so the REFCNT_DEC() below will drop to 0.
125 * We must be careful not to introduce inter-message references or this
126 * logic will fall apart..
129 refs = rtnl->rqueue_size + rtnl->wqueue_size + 1;
131 if (REFCNT_GET(rtnl->n_ref) <= refs) {
132 struct match_callback *f;
136 for (i = 0; i < rtnl->rqueue_size; i++) {
137 if (REFCNT_GET(rtnl->rqueue[i]->n_ref) > 1) {
140 } else if (rtnl->rqueue[i]->rtnl != rtnl)
145 for (i = 0; i < rtnl->wqueue_size; i++) {
146 if (REFCNT_GET(rtnl->wqueue[i]->n_ref) > 1) {
149 } else if (rtnl->wqueue[i]->rtnl != rtnl)
154 if (q && REFCNT_GET(rtnl->n_ref) == refs) {
155 /* Drop our own ref early to avoid recursion from:
156 * sd_rtnl_message_unref()
158 * These must enter sd_rtnl_unref() with a ref-cnt
159 * smaller than us. */
160 REFCNT_DEC(rtnl->n_ref);
162 for (i = 0; i < rtnl->rqueue_size; i++)
163 sd_rtnl_message_unref(rtnl->rqueue[i]);
166 for (i = 0; i < rtnl->wqueue_size; i++)
167 sd_rtnl_message_unref(rtnl->wqueue[i]);
170 assert_se(REFCNT_GET(rtnl->n_ref) == 0);
172 hashmap_free_free(rtnl->reply_callbacks);
173 prioq_free(rtnl->reply_callbacks_prioq);
175 while ((f = rtnl->match_callbacks)) {
176 LIST_REMOVE(match_callbacks, rtnl->match_callbacks, f);
180 safe_close(rtnl->fd);
182 sd_event_source_unref(rtnl->io_event_source);
183 sd_event_source_unref(rtnl->time_event_source);
184 sd_event_source_unref(rtnl->exit_event_source);
185 sd_event_unref(rtnl->event);
193 assert_se(REFCNT_GET(rtnl->n_ref) > 0);
194 REFCNT_DEC(rtnl->n_ref);
199 static void rtnl_seal_message(sd_rtnl *rtnl, sd_rtnl_message *m) {
201 assert(!rtnl_pid_changed(rtnl));
205 m->hdr->nlmsg_seq = rtnl->serial++;
207 rtnl_message_seal(m);
212 int sd_rtnl_send(sd_rtnl *nl,
213 sd_rtnl_message *message,
217 assert_return(nl, -EINVAL);
218 assert_return(!rtnl_pid_changed(nl), -ECHILD);
219 assert_return(message, -EINVAL);
220 assert_return(!message->sealed, -EPERM);
222 rtnl_seal_message(nl, message);
224 if (nl->wqueue_size <= 0) {
226 r = socket_write_message(nl, message);
230 /* nothing was sent, so let's put it on
232 nl->wqueue[0] = sd_rtnl_message_ref(message);
238 /* append to queue */
239 if (nl->wqueue_size >= RTNL_WQUEUE_MAX)
242 q = realloc(nl->wqueue, sizeof(sd_rtnl_message*) * (nl->wqueue_size + 1));
247 q[nl->wqueue_size ++] = sd_rtnl_message_ref(message);
251 *serial = rtnl_message_get_serial(message);
256 static int dispatch_rqueue(sd_rtnl *rtnl, sd_rtnl_message **message) {
257 sd_rtnl_message *z = NULL;
263 if (rtnl->rqueue_size > 0) {
264 /* Dispatch a queued message */
266 *message = rtnl->rqueue[0];
267 rtnl->rqueue_size --;
268 memmove(rtnl->rqueue, rtnl->rqueue + 1, sizeof(sd_rtnl_message*) * rtnl->rqueue_size);
273 /* Try to read a new message */
274 r = socket_read_message(rtnl, &z);
283 static int dispatch_wqueue(sd_rtnl *rtnl) {
288 while (rtnl->wqueue_size > 0) {
289 r = socket_write_message(rtnl, rtnl->wqueue[0]);
293 /* Didn't do anything this time */
296 /* see equivalent in sd-bus.c */
297 sd_rtnl_message_unref(rtnl->wqueue[0]);
298 rtnl->wqueue_size --;
299 memmove(rtnl->wqueue, rtnl->wqueue + 1, sizeof(sd_rtnl_message*) * rtnl->wqueue_size);
308 static int process_timeout(sd_rtnl *rtnl) {
309 _cleanup_rtnl_message_unref_ sd_rtnl_message *m = NULL;
310 struct reply_callback *c;
316 c = prioq_peek(rtnl->reply_callbacks_prioq);
320 n = now(CLOCK_MONOTONIC);
324 r = rtnl_message_new_synthetic_error(-ETIMEDOUT, c->serial, &m);
328 assert_se(prioq_pop(rtnl->reply_callbacks_prioq) == c);
329 hashmap_remove(rtnl->reply_callbacks, &c->serial);
331 r = c->callback(rtnl, m, c->userdata);
334 return r < 0 ? r : 1;
337 static int process_reply(sd_rtnl *rtnl, sd_rtnl_message *m) {
338 struct reply_callback *c;
345 if (sd_rtnl_message_is_broadcast(m))
348 serial = rtnl_message_get_serial(m);
349 c = hashmap_remove(rtnl->reply_callbacks, &serial);
354 prioq_remove(rtnl->reply_callbacks_prioq, c, &c->prioq_idx);
356 r = c->callback(rtnl, m, c->userdata);
362 static int process_match(sd_rtnl *rtnl, sd_rtnl_message *m) {
363 struct match_callback *c;
370 r = sd_rtnl_message_get_type(m, &type);
374 LIST_FOREACH(match_callbacks, c, rtnl->match_callbacks) {
375 if (type == c->type) {
376 r = c->callback(rtnl, m, c->userdata);
385 static int process_running(sd_rtnl *rtnl, sd_rtnl_message **ret) {
386 _cleanup_rtnl_message_unref_ sd_rtnl_message *m = NULL;
391 r = process_timeout(rtnl);
395 r = dispatch_wqueue(rtnl);
399 r = dispatch_rqueue(rtnl, &m);
405 r = process_reply(rtnl, m);
409 r = process_match(rtnl, m);
429 int sd_rtnl_process(sd_rtnl *rtnl, sd_rtnl_message **ret) {
430 RTNL_DONT_DESTROY(rtnl);
433 assert_return(rtnl, -EINVAL);
434 assert_return(!rtnl_pid_changed(rtnl), -ECHILD);
435 assert_return(!rtnl->processing, -EBUSY);
437 rtnl->processing = true;
438 r = process_running(rtnl, ret);
439 rtnl->processing = false;
444 static usec_t calc_elapse(uint64_t usec) {
445 if (usec == (uint64_t) -1)
449 usec = RTNL_DEFAULT_TIMEOUT;
451 return now(CLOCK_MONOTONIC) + usec;
454 static int rtnl_poll(sd_rtnl *rtnl, bool need_more, uint64_t timeout_usec) {
455 struct pollfd p[1] = {};
457 usec_t m = (usec_t) -1;
462 e = sd_rtnl_get_events(rtnl);
467 /* Caller wants more data, and doesn't care about
468 * what's been read or any other timeouts. */
472 /* Caller wants to process if there is something to
473 * process, but doesn't care otherwise */
475 r = sd_rtnl_get_timeout(rtnl, &until);
480 nw = now(CLOCK_MONOTONIC);
481 m = until > nw ? until - nw : 0;
485 if (timeout_usec != (uint64_t) -1 && (m == (uint64_t) -1 || timeout_usec < m))
491 r = ppoll(p, 1, m == (uint64_t) -1 ? NULL : timespec_store(&ts, m), NULL);
495 return r > 0 ? 1 : 0;
498 int sd_rtnl_wait(sd_rtnl *nl, uint64_t timeout_usec) {
499 assert_return(nl, -EINVAL);
500 assert_return(!rtnl_pid_changed(nl), -ECHILD);
502 if (nl->rqueue_size > 0)
505 return rtnl_poll(nl, false, timeout_usec);
508 static int timeout_compare(const void *a, const void *b) {
509 const struct reply_callback *x = a, *y = b;
511 if (x->timeout != 0 && y->timeout == 0)
514 if (x->timeout == 0 && y->timeout != 0)
517 if (x->timeout < y->timeout)
520 if (x->timeout > y->timeout)
526 int sd_rtnl_call_async(sd_rtnl *nl,
528 sd_rtnl_message_handler_t callback,
532 struct reply_callback *c;
536 assert_return(nl, -EINVAL);
537 assert_return(m, -EINVAL);
538 assert_return(callback, -EINVAL);
539 assert_return(!rtnl_pid_changed(nl), -ECHILD);
541 r = hashmap_ensure_allocated(&nl->reply_callbacks, uint64_hash_func, uint64_compare_func);
545 if (usec != (uint64_t) -1) {
546 r = prioq_ensure_allocated(&nl->reply_callbacks_prioq, timeout_compare);
551 c = new0(struct reply_callback, 1);
555 c->callback = callback;
556 c->userdata = userdata;
557 c->timeout = calc_elapse(usec);
559 k = sd_rtnl_send(nl, m, &s);
567 r = hashmap_put(nl->reply_callbacks, &c->serial, c);
573 if (c->timeout != 0) {
574 r = prioq_put(nl->reply_callbacks_prioq, c, &c->prioq_idx);
577 sd_rtnl_call_async_cancel(nl, c->serial);
588 int sd_rtnl_call_async_cancel(sd_rtnl *nl, uint32_t serial) {
589 struct reply_callback *c;
592 assert_return(nl, -EINVAL);
593 assert_return(serial != 0, -EINVAL);
594 assert_return(!rtnl_pid_changed(nl), -ECHILD);
596 c = hashmap_remove(nl->reply_callbacks, &s);
601 prioq_remove(nl->reply_callbacks_prioq, c, &c->prioq_idx);
607 int sd_rtnl_call(sd_rtnl *nl,
608 sd_rtnl_message *message,
610 sd_rtnl_message **ret) {
616 assert_return(nl, -EINVAL);
617 assert_return(!rtnl_pid_changed(nl), -ECHILD);
618 assert_return(message, -EINVAL);
620 r = sd_rtnl_send(nl, message, &serial);
624 timeout = calc_elapse(usec);
628 _cleanup_rtnl_message_unref_ sd_rtnl_message *incoming = NULL;
633 if (nl->rqueue_size >= RTNL_RQUEUE_MAX)
636 /* Make sure there's room for queueing this
637 * locally, before we read the message */
639 q = realloc(nl->rqueue, (nl->rqueue_size + 1) * sizeof(sd_rtnl_message*));
647 r = socket_read_message(nl, &incoming);
651 uint32_t received_serial = rtnl_message_get_serial(incoming);
653 if (received_serial == serial) {
654 r = sd_rtnl_message_get_errno(incoming);
666 /* Room was allocated on the queue above */
667 nl->rqueue[nl->rqueue_size ++] = incoming;
671 /* Try to read more, right away */
680 n = now(CLOCK_MONOTONIC);
686 left = (uint64_t) -1;
688 r = rtnl_poll(nl, true, left);
692 r = dispatch_wqueue(nl);
698 int sd_rtnl_flush(sd_rtnl *rtnl) {
701 assert_return(rtnl, -EINVAL);
702 assert_return(!rtnl_pid_changed(rtnl), -ECHILD);
704 if (rtnl->wqueue_size <= 0)
708 r = dispatch_wqueue(rtnl);
712 if (rtnl->wqueue_size <= 0)
715 r = rtnl_poll(rtnl, false, (uint64_t) -1);
721 int sd_rtnl_get_events(sd_rtnl *rtnl) {
724 assert_return(rtnl, -EINVAL);
725 assert_return(!rtnl_pid_changed(rtnl), -ECHILD);
727 if (rtnl->rqueue_size <= 0)
729 if (rtnl->wqueue_size > 0)
735 int sd_rtnl_get_timeout(sd_rtnl *rtnl, uint64_t *timeout_usec) {
736 struct reply_callback *c;
738 assert_return(rtnl, -EINVAL);
739 assert_return(timeout_usec, -EINVAL);
740 assert_return(!rtnl_pid_changed(rtnl), -ECHILD);
742 if (rtnl->rqueue_size > 0) {
747 c = prioq_peek(rtnl->reply_callbacks_prioq);
749 *timeout_usec = (uint64_t) -1;
753 *timeout_usec = c->timeout;
758 static int io_callback(sd_event_source *s, int fd, uint32_t revents, void *userdata) {
759 sd_rtnl *rtnl = userdata;
764 r = sd_rtnl_process(rtnl, NULL);
771 static int time_callback(sd_event_source *s, uint64_t usec, void *userdata) {
772 sd_rtnl *rtnl = userdata;
777 r = sd_rtnl_process(rtnl, NULL);
784 static int prepare_callback(sd_event_source *s, void *userdata) {
785 sd_rtnl *rtnl = userdata;
792 e = sd_rtnl_get_events(rtnl);
796 r = sd_event_source_set_io_events(rtnl->io_event_source, e);
800 r = sd_rtnl_get_timeout(rtnl, &until);
806 j = sd_event_source_set_time(rtnl->time_event_source, until);
811 r = sd_event_source_set_enabled(rtnl->time_event_source, r > 0);
818 static int exit_callback(sd_event_source *event, void *userdata) {
819 sd_rtnl *rtnl = userdata;
828 int sd_rtnl_attach_event(sd_rtnl *rtnl, sd_event *event, int priority) {
831 assert_return(rtnl, -EINVAL);
832 assert_return(!rtnl->event, -EBUSY);
834 assert(!rtnl->io_event_source);
835 assert(!rtnl->time_event_source);
838 rtnl->event = sd_event_ref(event);
840 r = sd_event_default(&rtnl->event);
845 r = sd_event_add_io(rtnl->event, &rtnl->io_event_source, rtnl->fd, 0, io_callback, rtnl);
849 r = sd_event_source_set_priority(rtnl->io_event_source, priority);
853 r = sd_event_source_set_prepare(rtnl->io_event_source, prepare_callback);
857 r = sd_event_add_time(rtnl->event, &rtnl->time_event_source, CLOCK_MONOTONIC, 0, 0, time_callback, rtnl);
861 r = sd_event_source_set_priority(rtnl->time_event_source, priority);
865 r = sd_event_add_exit(rtnl->event, &rtnl->exit_event_source, exit_callback, rtnl);
872 sd_rtnl_detach_event(rtnl);
876 int sd_rtnl_detach_event(sd_rtnl *rtnl) {
877 assert_return(rtnl, -EINVAL);
878 assert_return(rtnl->event, -ENXIO);
880 if (rtnl->io_event_source)
881 rtnl->io_event_source = sd_event_source_unref(rtnl->io_event_source);
883 if (rtnl->time_event_source)
884 rtnl->time_event_source = sd_event_source_unref(rtnl->time_event_source);
886 if (rtnl->exit_event_source)
887 rtnl->exit_event_source = sd_event_source_unref(rtnl->exit_event_source);
890 rtnl->event = sd_event_unref(rtnl->event);
895 int sd_rtnl_add_match(sd_rtnl *rtnl,
897 sd_rtnl_message_handler_t callback,
899 struct match_callback *c;
901 assert_return(rtnl, -EINVAL);
902 assert_return(callback, -EINVAL);
903 assert_return(!rtnl_pid_changed(rtnl), -ECHILD);
904 assert_return(rtnl_message_type_is_link(type) ||
905 rtnl_message_type_is_addr(type) ||
906 rtnl_message_type_is_route(type), -ENOTSUP);
908 c = new0(struct match_callback, 1);
912 c->callback = callback;
914 c->userdata = userdata;
916 LIST_PREPEND(match_callbacks, rtnl->match_callbacks, c);
921 int sd_rtnl_remove_match(sd_rtnl *rtnl,
923 sd_rtnl_message_handler_t callback,
925 struct match_callback *c;
927 assert_return(rtnl, -EINVAL);
928 assert_return(callback, -EINVAL);
929 assert_return(!rtnl_pid_changed(rtnl), -ECHILD);
931 LIST_FOREACH(match_callbacks, c, rtnl->match_callbacks)
932 if (c->callback == callback && c->type == type && c->userdata == userdata) {
933 LIST_REMOVE(match_callbacks, rtnl->match_callbacks, c);