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(uint32_t groups, sd_rtnl **ret) {
74 _cleanup_sd_rtnl_unref_ sd_rtnl *rtnl = NULL;
78 r = sd_rtnl_new(&rtnl);
82 rtnl->fd = socket(PF_NETLINK, SOCK_RAW|SOCK_CLOEXEC|SOCK_NONBLOCK, NETLINK_ROUTE);
86 rtnl->sockaddr.nl.nl_groups = groups;
88 addrlen = sizeof(rtnl->sockaddr);
90 r = bind(rtnl->fd, &rtnl->sockaddr.sa, addrlen);
94 r = getsockname(rtnl->fd, &rtnl->sockaddr.sa, &addrlen);
104 sd_rtnl *sd_rtnl_ref(sd_rtnl *rtnl) {
106 assert_se(REFCNT_INC(rtnl->n_ref) >= 2);
111 sd_rtnl *sd_rtnl_unref(sd_rtnl *rtnl) {
113 if (rtnl && REFCNT_DEC(rtnl->n_ref) <= 0) {
114 struct match_callback *f;
117 for (i = 0; i < rtnl->rqueue_size; i++)
118 sd_rtnl_message_unref(rtnl->rqueue[i]);
121 for (i = 0; i < rtnl->wqueue_size; i++)
122 sd_rtnl_message_unref(rtnl->wqueue[i]);
125 hashmap_free_free(rtnl->reply_callbacks);
126 prioq_free(rtnl->reply_callbacks_prioq);
128 while ((f = rtnl->match_callbacks)) {
129 LIST_REMOVE(match_callbacks, rtnl->match_callbacks, f);
134 close_nointr_nofail(rtnl->fd);
142 int sd_rtnl_send(sd_rtnl *nl,
143 sd_rtnl_message *message,
147 assert_return(nl, -EINVAL);
148 assert_return(!rtnl_pid_changed(nl), -ECHILD);
149 assert_return(message, -EINVAL);
151 r = message_seal(nl, message);
155 if (nl->wqueue_size <= 0) {
157 r = socket_write_message(nl, message);
161 /* nothing was sent, so let's put it on
163 nl->wqueue[0] = sd_rtnl_message_ref(message);
169 /* append to queue */
170 if (nl->wqueue_size >= RTNL_WQUEUE_MAX)
173 q = realloc(nl->wqueue, sizeof(sd_rtnl_message*) * (nl->wqueue_size + 1));
178 q[nl->wqueue_size ++] = sd_rtnl_message_ref(message);
182 *serial = message_get_serial(message);
187 static int dispatch_rqueue(sd_rtnl *rtnl, sd_rtnl_message **message) {
188 sd_rtnl_message *z = NULL;
194 if (rtnl->rqueue_size > 0) {
195 /* Dispatch a queued message */
197 *message = rtnl->rqueue[0];
198 rtnl->rqueue_size --;
199 memmove(rtnl->rqueue, rtnl->rqueue + 1, sizeof(sd_rtnl_message*) * rtnl->rqueue_size);
204 /* Try to read a new message */
205 r = socket_read_message(rtnl, &z);
216 static int dispatch_wqueue(sd_rtnl *rtnl) {
221 while (rtnl->wqueue_size > 0) {
222 r = socket_write_message(rtnl, rtnl->wqueue[0]);
226 /* Didn't do anything this time */
229 /* see equivalent in sd-bus.c */
230 sd_rtnl_message_unref(rtnl->wqueue[0]);
231 rtnl->wqueue_size --;
232 memmove(rtnl->wqueue, rtnl->wqueue + 1, sizeof(sd_rtnl_message*) * rtnl->wqueue_size);
241 static int process_timeout(sd_rtnl *rtnl) {
242 _cleanup_sd_rtnl_message_unref_ sd_rtnl_message *m = NULL;
243 struct reply_callback *c;
249 c = prioq_peek(rtnl->reply_callbacks_prioq);
253 n = now(CLOCK_MONOTONIC);
257 r = message_new_synthetic_error(-ETIMEDOUT, c->serial, &m);
261 assert_se(prioq_pop(rtnl->reply_callbacks_prioq) == c);
262 hashmap_remove(rtnl->reply_callbacks, &c->serial);
264 r = c->callback(rtnl, m, c->userdata);
267 return r < 0 ? r : 1;
270 static int process_reply(sd_rtnl *rtnl, sd_rtnl_message *m) {
271 struct reply_callback *c;
278 serial = message_get_serial(m);
279 c = hashmap_remove(rtnl->reply_callbacks, &serial);
284 prioq_remove(rtnl->reply_callbacks_prioq, c, &c->prioq_idx);
286 r = c->callback(rtnl, m, c->userdata);
292 static int process_match(sd_rtnl *rtnl, sd_rtnl_message *m) {
293 struct match_callback *c;
300 r = sd_rtnl_message_get_type(m, &type);
304 LIST_FOREACH(match_callbacks, c, rtnl->match_callbacks) {
305 if (type & c->types) {
306 r = c->callback(rtnl, m, c->userdata);
315 static int process_running(sd_rtnl *rtnl, sd_rtnl_message **ret) {
316 _cleanup_sd_rtnl_message_unref_ sd_rtnl_message *m = NULL;
319 r = process_timeout(rtnl);
323 r = dispatch_wqueue(rtnl);
327 r = dispatch_rqueue(rtnl, &m);
333 r = process_reply(rtnl, m);
337 r = process_match(rtnl, m);
357 int sd_rtnl_process(sd_rtnl *rtnl, sd_rtnl_message **ret) {
358 RTNL_DONT_DESTROY(rtnl);
361 assert_return(rtnl, -EINVAL);
362 assert_return(!rtnl_pid_changed(rtnl), -ECHILD);
363 assert_return(!rtnl->processing, -EBUSY);
365 rtnl->processing = true;
366 r = process_running(rtnl, ret);
367 rtnl->processing = false;
372 static usec_t calc_elapse(uint64_t usec) {
373 if (usec == (uint64_t) -1)
377 usec = RTNL_DEFAULT_TIMEOUT;
379 return now(CLOCK_MONOTONIC) + usec;
382 static int rtnl_poll(sd_rtnl *rtnl, bool need_more, uint64_t timeout_usec) {
383 struct pollfd p[1] = {};
385 usec_t m = (usec_t) -1;
390 e = sd_rtnl_get_events(rtnl);
395 /* Caller wants more data, and doesn't care about
396 * what's been read or any other timeouts. */
400 /* Caller wants to process if there is something to
401 * process, but doesn't care otherwise */
403 r = sd_rtnl_get_timeout(rtnl, &until);
408 nw = now(CLOCK_MONOTONIC);
409 m = until > nw ? until - nw : 0;
413 if (timeout_usec != (uint64_t) -1 && (m == (uint64_t) -1 || timeout_usec < m))
419 r = ppoll(p, 1, m == (uint64_t) -1 ? NULL : timespec_store(&ts, m), NULL);
423 return r > 0 ? 1 : 0;
426 int sd_rtnl_wait(sd_rtnl *nl, uint64_t timeout_usec) {
427 assert_return(nl, -EINVAL);
428 assert_return(!rtnl_pid_changed(nl), -ECHILD);
430 if (nl->rqueue_size > 0)
433 return rtnl_poll(nl, false, timeout_usec);
436 static int timeout_compare(const void *a, const void *b) {
437 const struct reply_callback *x = a, *y = b;
439 if (x->timeout != 0 && y->timeout == 0)
442 if (x->timeout == 0 && y->timeout != 0)
445 if (x->timeout < y->timeout)
448 if (x->timeout > y->timeout)
454 int sd_rtnl_call_async(sd_rtnl *nl,
456 sd_rtnl_message_handler_t callback,
460 struct reply_callback *c;
464 assert_return(nl, -EINVAL);
465 assert_return(m, -EINVAL);
466 assert_return(callback, -EINVAL);
467 assert_return(!rtnl_pid_changed(nl), -ECHILD);
469 r = hashmap_ensure_allocated(&nl->reply_callbacks, uint64_hash_func, uint64_compare_func);
473 if (usec != (uint64_t) -1) {
474 r = prioq_ensure_allocated(&nl->reply_callbacks_prioq, timeout_compare);
479 c = new0(struct reply_callback, 1);
483 c->callback = callback;
484 c->userdata = userdata;
485 c->timeout = calc_elapse(usec);
487 k = sd_rtnl_send(nl, m, &s);
495 r = hashmap_put(nl->reply_callbacks, &c->serial, c);
501 if (c->timeout != 0) {
502 r = prioq_put(nl->reply_callbacks_prioq, c, &c->prioq_idx);
505 sd_rtnl_call_async_cancel(nl, c->serial);
516 int sd_rtnl_call_async_cancel(sd_rtnl *nl, uint32_t serial) {
517 struct reply_callback *c;
520 assert_return(nl, -EINVAL);
521 assert_return(serial != 0, -EINVAL);
522 assert_return(!rtnl_pid_changed(nl), -ECHILD);
524 c = hashmap_remove(nl->reply_callbacks, &s);
529 prioq_remove(nl->reply_callbacks_prioq, c, &c->prioq_idx);
535 int sd_rtnl_call(sd_rtnl *nl,
536 sd_rtnl_message *message,
538 sd_rtnl_message **ret) {
544 assert_return(nl, -EINVAL);
545 assert_return(!rtnl_pid_changed(nl), -ECHILD);
546 assert_return(message, -EINVAL);
548 r = sd_rtnl_send(nl, message, &serial);
552 timeout = calc_elapse(usec);
556 _cleanup_sd_rtnl_message_unref_ sd_rtnl_message *incoming = NULL;
561 if (nl->rqueue_size >= RTNL_RQUEUE_MAX)
564 /* Make sure there's room for queueing this
565 * locally, before we read the message */
567 q = realloc(nl->rqueue, (nl->rqueue_size + 1) * sizeof(sd_rtnl_message*));
575 r = socket_read_message(nl, &incoming);
579 uint32_t received_serial = message_get_serial(incoming);
581 if (received_serial == serial) {
582 r = sd_rtnl_message_get_errno(incoming);
594 /* Room was allocated on the queue above */
595 nl->rqueue[nl->rqueue_size ++] = incoming;
599 /* Try to read more, right away */
608 n = now(CLOCK_MONOTONIC);
614 left = (uint64_t) -1;
616 r = rtnl_poll(nl, true, left);
620 r = dispatch_wqueue(nl);
626 int sd_rtnl_flush(sd_rtnl *rtnl) {
629 assert_return(rtnl, -EINVAL);
630 assert_return(!rtnl_pid_changed(rtnl), -ECHILD);
632 if (rtnl->wqueue_size <= 0)
636 r = dispatch_wqueue(rtnl);
640 if (rtnl->wqueue_size <= 0)
643 r = rtnl_poll(rtnl, false, (uint64_t) -1);
649 int sd_rtnl_get_events(sd_rtnl *rtnl) {
652 assert_return(rtnl, -EINVAL);
653 assert_return(!rtnl_pid_changed(rtnl), -ECHILD);
655 if (rtnl->rqueue_size <= 0)
657 if (rtnl->wqueue_size > 0)
663 int sd_rtnl_get_timeout(sd_rtnl *rtnl, uint64_t *timeout_usec) {
664 struct reply_callback *c;
666 assert_return(rtnl, -EINVAL);
667 assert_return(timeout_usec, -EINVAL);
668 assert_return(!rtnl_pid_changed(rtnl), -ECHILD);
670 if (rtnl->rqueue_size > 0) {
675 c = prioq_peek(rtnl->reply_callbacks_prioq);
677 *timeout_usec = (uint64_t) -1;
681 *timeout_usec = c->timeout;
686 static int io_callback(sd_event_source *s, int fd, uint32_t revents, void *userdata) {
687 sd_rtnl *rtnl = userdata;
692 r = sd_rtnl_process(rtnl, NULL);
699 static int time_callback(sd_event_source *s, uint64_t usec, void *userdata) {
700 sd_rtnl *rtnl = userdata;
705 r = sd_rtnl_process(rtnl, NULL);
712 static int prepare_callback(sd_event_source *s, void *userdata) {
713 sd_rtnl *rtnl = userdata;
720 e = sd_rtnl_get_events(rtnl);
724 r = sd_event_source_set_io_events(rtnl->io_event_source, e);
728 r = sd_rtnl_get_timeout(rtnl, &until);
734 j = sd_event_source_set_time(rtnl->time_event_source, until);
739 r = sd_event_source_set_enabled(rtnl->time_event_source, r > 0);
746 static int quit_callback(sd_event_source *event, void *userdata) {
747 sd_rtnl *rtnl = userdata;
756 int sd_rtnl_attach_event(sd_rtnl *rtnl, sd_event *event, int priority) {
759 assert_return(rtnl, -EINVAL);
760 assert_return(!rtnl->event, -EBUSY);
762 assert(!rtnl->io_event_source);
763 assert(!rtnl->time_event_source);
766 rtnl->event = sd_event_ref(event);
768 r = sd_event_default(&rtnl->event);
773 r = sd_event_add_io(rtnl->event, rtnl->fd, 0, io_callback, rtnl, &rtnl->io_event_source);
777 r = sd_event_source_set_priority(rtnl->io_event_source, priority);
781 r = sd_event_source_set_prepare(rtnl->io_event_source, prepare_callback);
785 r = sd_event_add_monotonic(rtnl->event, 0, 0, time_callback, rtnl, &rtnl->time_event_source);
789 r = sd_event_source_set_priority(rtnl->time_event_source, priority);
793 r = sd_event_add_quit(rtnl->event, quit_callback, rtnl, &rtnl->quit_event_source);
800 sd_rtnl_detach_event(rtnl);
804 int sd_rtnl_detach_event(sd_rtnl *rtnl) {
805 assert_return(rtnl, -EINVAL);
806 assert_return(rtnl->event, -ENXIO);
808 if (rtnl->io_event_source)
809 rtnl->io_event_source = sd_event_source_unref(rtnl->io_event_source);
811 if (rtnl->time_event_source)
812 rtnl->time_event_source = sd_event_source_unref(rtnl->time_event_source);
814 if (rtnl->quit_event_source)
815 rtnl->quit_event_source = sd_event_source_unref(rtnl->quit_event_source);
818 rtnl->event = sd_event_unref(rtnl->event);
823 int sd_rtnl_add_match(sd_rtnl *rtnl,
825 sd_rtnl_message_handler_t callback,
827 struct match_callback *c;
829 assert_return(rtnl, -EINVAL);
830 assert_return(callback, -EINVAL);
831 assert_return(types, -EINVAL);
832 assert_return(!rtnl_pid_changed(rtnl), -ECHILD);
834 c = new0(struct match_callback, 1);
838 c->callback = callback;
840 c->userdata = userdata;
842 LIST_PREPEND(match_callbacks, rtnl->match_callbacks, c);
847 int sd_rtnl_remove_match(sd_rtnl *rtnl,
849 sd_rtnl_message_handler_t callback,
851 struct match_callback *c;
853 assert_return(rtnl, -EINVAL);
854 assert_return(callback, -EINVAL);
855 assert_return(!rtnl_pid_changed(rtnl), -ECHILD);
857 LIST_FOREACH(match_callbacks, c, rtnl->match_callbacks)
858 if (c->callback == callback && c->types == types && c->userdata == userdata) {
859 LIST_REMOVE(match_callbacks, rtnl->match_callbacks, c);