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) {
34 _cleanup_rtnl_unref_ sd_rtnl *rtnl = NULL;
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 if (!GREEDY_REALLOC(rtnl->wqueue, rtnl->wqueue_allocated, 1))
57 /* We guarantee that the read buffer has at least space for
59 if (!greedy_realloc((void**)&rtnl->rbuffer, &rtnl->rbuffer_allocated,
60 sizeof(struct nlmsghdr), sizeof(uint8_t)))
69 static bool rtnl_pid_changed(sd_rtnl *rtnl) {
72 /* We don't support people creating an rtnl connection and
73 * keeping it around over a fork(). Let's complain. */
75 return rtnl->original_pid != getpid();
78 int sd_rtnl_open(sd_rtnl **ret, uint32_t groups) {
79 _cleanup_rtnl_unref_ sd_rtnl *rtnl = NULL;
83 assert_return(ret, -EINVAL);
85 r = sd_rtnl_new(&rtnl);
89 rtnl->fd = socket(PF_NETLINK, SOCK_RAW|SOCK_CLOEXEC|SOCK_NONBLOCK, NETLINK_ROUTE);
93 if (setsockopt(rtnl->fd, SOL_SOCKET, SO_PASSCRED, &one, sizeof(one)) < 0)
96 rtnl->sockaddr.nl.nl_groups = groups;
98 addrlen = sizeof(rtnl->sockaddr);
100 r = bind(rtnl->fd, &rtnl->sockaddr.sa, addrlen);
104 r = getsockname(rtnl->fd, &rtnl->sockaddr.sa, &addrlen);
114 sd_rtnl *sd_rtnl_ref(sd_rtnl *rtnl) {
115 assert_return(rtnl, NULL);
116 assert_return(!rtnl_pid_changed(rtnl), NULL);
119 assert_se(REFCNT_INC(rtnl->n_ref) >= 2);
124 sd_rtnl *sd_rtnl_unref(sd_rtnl *rtnl) {
128 assert_return(!rtnl_pid_changed(rtnl), NULL);
130 if (REFCNT_DEC(rtnl->n_ref) <= 0) {
131 struct match_callback *f;
134 for (i = 0; i < rtnl->rqueue_size; i++)
135 sd_rtnl_message_unref(rtnl->rqueue[i]);
138 for (i = 0; i < rtnl->rqueue_partial_size; i++)
139 sd_rtnl_message_unref(rtnl->rqueue_partial[i]);
140 free(rtnl->rqueue_partial);
142 for (i = 0; i < rtnl->wqueue_size; i++)
143 sd_rtnl_message_unref(rtnl->wqueue[i]);
148 hashmap_free_free(rtnl->reply_callbacks);
149 prioq_free(rtnl->reply_callbacks_prioq);
151 sd_event_source_unref(rtnl->io_event_source);
152 sd_event_source_unref(rtnl->time_event_source);
153 sd_event_source_unref(rtnl->exit_event_source);
154 sd_event_unref(rtnl->event);
156 while ((f = rtnl->match_callbacks)) {
157 LIST_REMOVE(match_callbacks, rtnl->match_callbacks, f);
161 safe_close(rtnl->fd);
168 static void rtnl_seal_message(sd_rtnl *rtnl, sd_rtnl_message *m) {
170 assert(!rtnl_pid_changed(rtnl));
174 m->hdr->nlmsg_seq = rtnl->serial++;
176 rtnl_message_seal(m);
181 int sd_rtnl_send(sd_rtnl *nl,
182 sd_rtnl_message *message,
186 assert_return(nl, -EINVAL);
187 assert_return(!rtnl_pid_changed(nl), -ECHILD);
188 assert_return(message, -EINVAL);
189 assert_return(!message->sealed, -EPERM);
191 rtnl_seal_message(nl, message);
193 if (nl->wqueue_size <= 0) {
195 r = socket_write_message(nl, message);
199 /* nothing was sent, so let's put it on
201 nl->wqueue[0] = sd_rtnl_message_ref(message);
205 /* append to queue */
206 if (nl->wqueue_size >= RTNL_WQUEUE_MAX)
209 if (!GREEDY_REALLOC(nl->wqueue, nl->wqueue_allocated, nl->wqueue_size + 1))
212 nl->wqueue[nl->wqueue_size ++] = sd_rtnl_message_ref(message);
216 *serial = rtnl_message_get_serial(message);
221 int rtnl_rqueue_make_room(sd_rtnl *rtnl) {
224 if (rtnl->rqueue_size >= RTNL_RQUEUE_MAX)
227 if (!GREEDY_REALLOC(rtnl->rqueue, rtnl->rqueue_allocated, rtnl->rqueue_size + 1))
233 int rtnl_rqueue_partial_make_room(sd_rtnl *rtnl) {
236 if (rtnl->rqueue_partial_size >= RTNL_RQUEUE_MAX)
239 if (!GREEDY_REALLOC(rtnl->rqueue_partial, rtnl->rqueue_partial_allocated,
240 rtnl->rqueue_partial_size + 1))
246 static int dispatch_rqueue(sd_rtnl *rtnl, sd_rtnl_message **message) {
252 if (rtnl->rqueue_size <= 0) {
253 /* Try to read a new message */
254 r = socket_read_message(rtnl);
259 /* Dispatch a queued message */
260 *message = rtnl->rqueue[0];
261 rtnl->rqueue_size --;
262 memmove(rtnl->rqueue, rtnl->rqueue + 1, sizeof(sd_rtnl_message*) * rtnl->rqueue_size);
267 static int dispatch_wqueue(sd_rtnl *rtnl) {
272 while (rtnl->wqueue_size > 0) {
273 r = socket_write_message(rtnl, rtnl->wqueue[0]);
277 /* Didn't do anything this time */
280 /* see equivalent in sd-bus.c */
281 sd_rtnl_message_unref(rtnl->wqueue[0]);
282 rtnl->wqueue_size --;
283 memmove(rtnl->wqueue, rtnl->wqueue + 1, sizeof(sd_rtnl_message*) * rtnl->wqueue_size);
292 static int process_timeout(sd_rtnl *rtnl) {
293 _cleanup_rtnl_message_unref_ sd_rtnl_message *m = NULL;
294 struct reply_callback *c;
300 c = prioq_peek(rtnl->reply_callbacks_prioq);
304 n = now(CLOCK_MONOTONIC);
308 r = rtnl_message_new_synthetic_error(-ETIMEDOUT, c->serial, &m);
312 assert_se(prioq_pop(rtnl->reply_callbacks_prioq) == c);
313 hashmap_remove(rtnl->reply_callbacks, &c->serial);
315 r = c->callback(rtnl, m, c->userdata);
318 return r < 0 ? r : 1;
321 static int process_reply(sd_rtnl *rtnl, sd_rtnl_message *m) {
322 struct reply_callback *c;
329 if (sd_rtnl_message_is_broadcast(m))
332 serial = rtnl_message_get_serial(m);
333 c = hashmap_remove(rtnl->reply_callbacks, &serial);
338 prioq_remove(rtnl->reply_callbacks_prioq, c, &c->prioq_idx);
340 r = c->callback(rtnl, m, c->userdata);
346 static int process_match(sd_rtnl *rtnl, sd_rtnl_message *m) {
347 struct match_callback *c;
354 r = sd_rtnl_message_get_type(m, &type);
358 LIST_FOREACH(match_callbacks, c, rtnl->match_callbacks) {
359 if (type == c->type) {
360 r = c->callback(rtnl, m, c->userdata);
369 static int process_running(sd_rtnl *rtnl, sd_rtnl_message **ret) {
370 _cleanup_rtnl_message_unref_ sd_rtnl_message *m = NULL;
375 r = process_timeout(rtnl);
379 r = dispatch_wqueue(rtnl);
383 r = dispatch_rqueue(rtnl, &m);
389 r = process_reply(rtnl, m);
393 r = process_match(rtnl, m);
413 int sd_rtnl_process(sd_rtnl *rtnl, sd_rtnl_message **ret) {
414 RTNL_DONT_DESTROY(rtnl);
417 assert_return(rtnl, -EINVAL);
418 assert_return(!rtnl_pid_changed(rtnl), -ECHILD);
419 assert_return(!rtnl->processing, -EBUSY);
421 rtnl->processing = true;
422 r = process_running(rtnl, ret);
423 rtnl->processing = false;
428 static usec_t calc_elapse(uint64_t usec) {
429 if (usec == (uint64_t) -1)
433 usec = RTNL_DEFAULT_TIMEOUT;
435 return now(CLOCK_MONOTONIC) + usec;
438 static int rtnl_poll(sd_rtnl *rtnl, bool need_more, uint64_t timeout_usec) {
439 struct pollfd p[1] = {};
441 usec_t m = (usec_t) -1;
446 e = sd_rtnl_get_events(rtnl);
451 /* Caller wants more data, and doesn't care about
452 * what's been read or any other timeouts. */
456 /* Caller wants to process if there is something to
457 * process, but doesn't care otherwise */
459 r = sd_rtnl_get_timeout(rtnl, &until);
464 nw = now(CLOCK_MONOTONIC);
465 m = until > nw ? until - nw : 0;
469 if (timeout_usec != (uint64_t) -1 && (m == (uint64_t) -1 || timeout_usec < m))
475 r = ppoll(p, 1, m == (uint64_t) -1 ? NULL : timespec_store(&ts, m), NULL);
479 return r > 0 ? 1 : 0;
482 int sd_rtnl_wait(sd_rtnl *nl, uint64_t timeout_usec) {
483 assert_return(nl, -EINVAL);
484 assert_return(!rtnl_pid_changed(nl), -ECHILD);
486 if (nl->rqueue_size > 0)
489 return rtnl_poll(nl, false, timeout_usec);
492 static int timeout_compare(const void *a, const void *b) {
493 const struct reply_callback *x = a, *y = b;
495 if (x->timeout != 0 && y->timeout == 0)
498 if (x->timeout == 0 && y->timeout != 0)
501 if (x->timeout < y->timeout)
504 if (x->timeout > y->timeout)
510 int sd_rtnl_call_async(sd_rtnl *nl,
512 sd_rtnl_message_handler_t callback,
516 struct reply_callback *c;
520 assert_return(nl, -EINVAL);
521 assert_return(m, -EINVAL);
522 assert_return(callback, -EINVAL);
523 assert_return(!rtnl_pid_changed(nl), -ECHILD);
525 r = hashmap_ensure_allocated(&nl->reply_callbacks, uint64_hash_func, uint64_compare_func);
529 if (usec != (uint64_t) -1) {
530 r = prioq_ensure_allocated(&nl->reply_callbacks_prioq, timeout_compare);
535 c = new0(struct reply_callback, 1);
539 c->callback = callback;
540 c->userdata = userdata;
541 c->timeout = calc_elapse(usec);
543 k = sd_rtnl_send(nl, m, &s);
551 r = hashmap_put(nl->reply_callbacks, &c->serial, c);
557 if (c->timeout != 0) {
558 r = prioq_put(nl->reply_callbacks_prioq, c, &c->prioq_idx);
561 sd_rtnl_call_async_cancel(nl, c->serial);
572 int sd_rtnl_call_async_cancel(sd_rtnl *nl, uint32_t serial) {
573 struct reply_callback *c;
576 assert_return(nl, -EINVAL);
577 assert_return(serial != 0, -EINVAL);
578 assert_return(!rtnl_pid_changed(nl), -ECHILD);
580 c = hashmap_remove(nl->reply_callbacks, &s);
585 prioq_remove(nl->reply_callbacks_prioq, c, &c->prioq_idx);
591 int sd_rtnl_call(sd_rtnl *rtnl,
592 sd_rtnl_message *message,
594 sd_rtnl_message **ret) {
600 assert_return(rtnl, -EINVAL);
601 assert_return(!rtnl_pid_changed(rtnl), -ECHILD);
602 assert_return(message, -EINVAL);
604 r = sd_rtnl_send(rtnl, message, &serial);
608 timeout = calc_elapse(usec);
613 while (i < rtnl->rqueue_size) {
614 sd_rtnl_message *incoming;
615 uint32_t received_serial;
617 incoming = rtnl->rqueue[i];
618 received_serial = rtnl_message_get_serial(incoming);
620 if (received_serial == serial) {
621 /* found a match, remove from rqueue and return it */
622 memmove(rtnl->rqueue + i,rtnl->rqueue + i + 1,
623 sizeof(sd_rtnl_message*) * (rtnl->rqueue_size - i - 1));
626 r = sd_rtnl_message_get_errno(incoming);
628 sd_rtnl_message_unref(incoming);
635 sd_rtnl_message_unref(incoming);
640 /* Try to read more, right away */
644 r = socket_read_message(rtnl);
648 /* receieved message, so try to process straight away */
654 n = now(CLOCK_MONOTONIC);
660 left = (uint64_t) -1;
662 r = rtnl_poll(rtnl, true, left);
666 r = dispatch_wqueue(rtnl);
672 int sd_rtnl_flush(sd_rtnl *rtnl) {
675 assert_return(rtnl, -EINVAL);
676 assert_return(!rtnl_pid_changed(rtnl), -ECHILD);
678 if (rtnl->wqueue_size <= 0)
682 r = dispatch_wqueue(rtnl);
686 if (rtnl->wqueue_size <= 0)
689 r = rtnl_poll(rtnl, false, (uint64_t) -1);
695 int sd_rtnl_get_events(sd_rtnl *rtnl) {
698 assert_return(rtnl, -EINVAL);
699 assert_return(!rtnl_pid_changed(rtnl), -ECHILD);
701 if (rtnl->rqueue_size <= 0)
703 if (rtnl->wqueue_size > 0)
709 int sd_rtnl_get_timeout(sd_rtnl *rtnl, uint64_t *timeout_usec) {
710 struct reply_callback *c;
712 assert_return(rtnl, -EINVAL);
713 assert_return(timeout_usec, -EINVAL);
714 assert_return(!rtnl_pid_changed(rtnl), -ECHILD);
716 if (rtnl->rqueue_size > 0) {
721 c = prioq_peek(rtnl->reply_callbacks_prioq);
723 *timeout_usec = (uint64_t) -1;
727 *timeout_usec = c->timeout;
732 static int io_callback(sd_event_source *s, int fd, uint32_t revents, void *userdata) {
733 sd_rtnl *rtnl = userdata;
738 r = sd_rtnl_process(rtnl, NULL);
745 static int time_callback(sd_event_source *s, uint64_t usec, void *userdata) {
746 sd_rtnl *rtnl = userdata;
751 r = sd_rtnl_process(rtnl, NULL);
758 static int prepare_callback(sd_event_source *s, void *userdata) {
759 sd_rtnl *rtnl = userdata;
766 e = sd_rtnl_get_events(rtnl);
770 r = sd_event_source_set_io_events(rtnl->io_event_source, e);
774 r = sd_rtnl_get_timeout(rtnl, &until);
780 j = sd_event_source_set_time(rtnl->time_event_source, until);
785 r = sd_event_source_set_enabled(rtnl->time_event_source, r > 0);
792 static int exit_callback(sd_event_source *event, void *userdata) {
793 sd_rtnl *rtnl = userdata;
802 int sd_rtnl_attach_event(sd_rtnl *rtnl, sd_event *event, int priority) {
805 assert_return(rtnl, -EINVAL);
806 assert_return(!rtnl->event, -EBUSY);
808 assert(!rtnl->io_event_source);
809 assert(!rtnl->time_event_source);
812 rtnl->event = sd_event_ref(event);
814 r = sd_event_default(&rtnl->event);
819 r = sd_event_add_io(rtnl->event, &rtnl->io_event_source, rtnl->fd, 0, io_callback, rtnl);
823 r = sd_event_source_set_priority(rtnl->io_event_source, priority);
827 r = sd_event_source_set_prepare(rtnl->io_event_source, prepare_callback);
831 r = sd_event_add_time(rtnl->event, &rtnl->time_event_source, CLOCK_MONOTONIC, 0, 0, time_callback, rtnl);
835 r = sd_event_source_set_priority(rtnl->time_event_source, priority);
839 r = sd_event_add_exit(rtnl->event, &rtnl->exit_event_source, exit_callback, rtnl);
846 sd_rtnl_detach_event(rtnl);
850 int sd_rtnl_detach_event(sd_rtnl *rtnl) {
851 assert_return(rtnl, -EINVAL);
852 assert_return(rtnl->event, -ENXIO);
854 if (rtnl->io_event_source)
855 rtnl->io_event_source = sd_event_source_unref(rtnl->io_event_source);
857 if (rtnl->time_event_source)
858 rtnl->time_event_source = sd_event_source_unref(rtnl->time_event_source);
860 if (rtnl->exit_event_source)
861 rtnl->exit_event_source = sd_event_source_unref(rtnl->exit_event_source);
864 rtnl->event = sd_event_unref(rtnl->event);
869 int sd_rtnl_add_match(sd_rtnl *rtnl,
871 sd_rtnl_message_handler_t callback,
873 struct match_callback *c;
875 assert_return(rtnl, -EINVAL);
876 assert_return(callback, -EINVAL);
877 assert_return(!rtnl_pid_changed(rtnl), -ECHILD);
878 assert_return(rtnl_message_type_is_link(type) ||
879 rtnl_message_type_is_addr(type) ||
880 rtnl_message_type_is_route(type), -ENOTSUP);
882 c = new0(struct match_callback, 1);
886 c->callback = callback;
888 c->userdata = userdata;
890 LIST_PREPEND(match_callbacks, rtnl->match_callbacks, c);
895 int sd_rtnl_remove_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);
905 LIST_FOREACH(match_callbacks, c, rtnl->match_callbacks)
906 if (c->callback == callback && c->type == type && c->userdata == userdata) {
907 LIST_REMOVE(match_callbacks, rtnl->match_callbacks, c);