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
);
187 assert_se(REFCNT_GET(rtnl
->n_ref
) > 0);
188 REFCNT_DEC(rtnl
->n_ref
);
193 static void rtnl_seal_message(sd_rtnl
*rtnl
, sd_rtnl_message
*m
) {
195 assert(!rtnl_pid_changed(rtnl
));
199 m
->hdr
->nlmsg_seq
= rtnl
->serial
++;
201 rtnl_message_seal(m
);
206 int sd_rtnl_send(sd_rtnl
*nl
,
207 sd_rtnl_message
*message
,
211 assert_return(nl
, -EINVAL
);
212 assert_return(!rtnl_pid_changed(nl
), -ECHILD
);
213 assert_return(message
, -EINVAL
);
214 assert_return(!message
->sealed
, -EPERM
);
216 rtnl_seal_message(nl
, message
);
218 if (nl
->wqueue_size
<= 0) {
220 r
= socket_write_message(nl
, message
);
224 /* nothing was sent, so let's put it on
226 nl
->wqueue
[0] = sd_rtnl_message_ref(message
);
232 /* append to queue */
233 if (nl
->wqueue_size
>= RTNL_WQUEUE_MAX
)
236 q
= realloc(nl
->wqueue
, sizeof(sd_rtnl_message
*) * (nl
->wqueue_size
+ 1));
241 q
[nl
->wqueue_size
++] = sd_rtnl_message_ref(message
);
245 *serial
= rtnl_message_get_serial(message
);
250 static int dispatch_rqueue(sd_rtnl
*rtnl
, sd_rtnl_message
**message
) {
251 sd_rtnl_message
*z
= NULL
;
257 if (rtnl
->rqueue_size
> 0) {
258 /* 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 /* Try to read a new message */
268 r
= socket_read_message(rtnl
, &z
);
277 static int dispatch_wqueue(sd_rtnl
*rtnl
) {
282 while (rtnl
->wqueue_size
> 0) {
283 r
= socket_write_message(rtnl
, rtnl
->wqueue
[0]);
287 /* Didn't do anything this time */
290 /* see equivalent in sd-bus.c */
291 sd_rtnl_message_unref(rtnl
->wqueue
[0]);
292 rtnl
->wqueue_size
--;
293 memmove(rtnl
->wqueue
, rtnl
->wqueue
+ 1, sizeof(sd_rtnl_message
*) * rtnl
->wqueue_size
);
302 static int process_timeout(sd_rtnl
*rtnl
) {
303 _cleanup_rtnl_message_unref_ sd_rtnl_message
*m
= NULL
;
304 struct reply_callback
*c
;
310 c
= prioq_peek(rtnl
->reply_callbacks_prioq
);
314 n
= now(CLOCK_MONOTONIC
);
318 r
= rtnl_message_new_synthetic_error(-ETIMEDOUT
, c
->serial
, &m
);
322 assert_se(prioq_pop(rtnl
->reply_callbacks_prioq
) == c
);
323 hashmap_remove(rtnl
->reply_callbacks
, &c
->serial
);
325 r
= c
->callback(rtnl
, m
, c
->userdata
);
328 return r
< 0 ? r
: 1;
331 static int process_reply(sd_rtnl
*rtnl
, sd_rtnl_message
*m
) {
332 struct reply_callback
*c
;
339 if (sd_rtnl_message_is_broadcast(m
))
342 serial
= rtnl_message_get_serial(m
);
343 c
= hashmap_remove(rtnl
->reply_callbacks
, &serial
);
348 prioq_remove(rtnl
->reply_callbacks_prioq
, c
, &c
->prioq_idx
);
350 r
= c
->callback(rtnl
, m
, c
->userdata
);
356 static int process_match(sd_rtnl
*rtnl
, sd_rtnl_message
*m
) {
357 struct match_callback
*c
;
364 r
= sd_rtnl_message_get_type(m
, &type
);
368 LIST_FOREACH(match_callbacks
, c
, rtnl
->match_callbacks
) {
369 if (type
== c
->type
) {
370 r
= c
->callback(rtnl
, m
, c
->userdata
);
379 static int process_running(sd_rtnl
*rtnl
, sd_rtnl_message
**ret
) {
380 _cleanup_rtnl_message_unref_ sd_rtnl_message
*m
= NULL
;
385 r
= process_timeout(rtnl
);
389 r
= dispatch_wqueue(rtnl
);
393 r
= dispatch_rqueue(rtnl
, &m
);
399 r
= process_reply(rtnl
, m
);
403 r
= process_match(rtnl
, m
);
423 int sd_rtnl_process(sd_rtnl
*rtnl
, sd_rtnl_message
**ret
) {
424 RTNL_DONT_DESTROY(rtnl
);
427 assert_return(rtnl
, -EINVAL
);
428 assert_return(!rtnl_pid_changed(rtnl
), -ECHILD
);
429 assert_return(!rtnl
->processing
, -EBUSY
);
431 rtnl
->processing
= true;
432 r
= process_running(rtnl
, ret
);
433 rtnl
->processing
= false;
438 static usec_t
calc_elapse(uint64_t usec
) {
439 if (usec
== (uint64_t) -1)
443 usec
= RTNL_DEFAULT_TIMEOUT
;
445 return now(CLOCK_MONOTONIC
) + usec
;
448 static int rtnl_poll(sd_rtnl
*rtnl
, bool need_more
, uint64_t timeout_usec
) {
449 struct pollfd p
[1] = {};
451 usec_t m
= (usec_t
) -1;
456 e
= sd_rtnl_get_events(rtnl
);
461 /* Caller wants more data, and doesn't care about
462 * what's been read or any other timeouts. */
466 /* Caller wants to process if there is something to
467 * process, but doesn't care otherwise */
469 r
= sd_rtnl_get_timeout(rtnl
, &until
);
474 nw
= now(CLOCK_MONOTONIC
);
475 m
= until
> nw
? until
- nw
: 0;
479 if (timeout_usec
!= (uint64_t) -1 && (m
== (uint64_t) -1 || timeout_usec
< m
))
485 r
= ppoll(p
, 1, m
== (uint64_t) -1 ? NULL
: timespec_store(&ts
, m
), NULL
);
489 return r
> 0 ? 1 : 0;
492 int sd_rtnl_wait(sd_rtnl
*nl
, uint64_t timeout_usec
) {
493 assert_return(nl
, -EINVAL
);
494 assert_return(!rtnl_pid_changed(nl
), -ECHILD
);
496 if (nl
->rqueue_size
> 0)
499 return rtnl_poll(nl
, false, timeout_usec
);
502 static int timeout_compare(const void *a
, const void *b
) {
503 const struct reply_callback
*x
= a
, *y
= b
;
505 if (x
->timeout
!= 0 && y
->timeout
== 0)
508 if (x
->timeout
== 0 && y
->timeout
!= 0)
511 if (x
->timeout
< y
->timeout
)
514 if (x
->timeout
> y
->timeout
)
520 int sd_rtnl_call_async(sd_rtnl
*nl
,
522 sd_rtnl_message_handler_t callback
,
526 struct reply_callback
*c
;
530 assert_return(nl
, -EINVAL
);
531 assert_return(m
, -EINVAL
);
532 assert_return(callback
, -EINVAL
);
533 assert_return(!rtnl_pid_changed(nl
), -ECHILD
);
535 r
= hashmap_ensure_allocated(&nl
->reply_callbacks
, uint64_hash_func
, uint64_compare_func
);
539 if (usec
!= (uint64_t) -1) {
540 r
= prioq_ensure_allocated(&nl
->reply_callbacks_prioq
, timeout_compare
);
545 c
= new0(struct reply_callback
, 1);
549 c
->callback
= callback
;
550 c
->userdata
= userdata
;
551 c
->timeout
= calc_elapse(usec
);
553 k
= sd_rtnl_send(nl
, m
, &s
);
561 r
= hashmap_put(nl
->reply_callbacks
, &c
->serial
, c
);
567 if (c
->timeout
!= 0) {
568 r
= prioq_put(nl
->reply_callbacks_prioq
, c
, &c
->prioq_idx
);
571 sd_rtnl_call_async_cancel(nl
, c
->serial
);
582 int sd_rtnl_call_async_cancel(sd_rtnl
*nl
, uint32_t serial
) {
583 struct reply_callback
*c
;
586 assert_return(nl
, -EINVAL
);
587 assert_return(serial
!= 0, -EINVAL
);
588 assert_return(!rtnl_pid_changed(nl
), -ECHILD
);
590 c
= hashmap_remove(nl
->reply_callbacks
, &s
);
595 prioq_remove(nl
->reply_callbacks_prioq
, c
, &c
->prioq_idx
);
601 int sd_rtnl_call(sd_rtnl
*nl
,
602 sd_rtnl_message
*message
,
604 sd_rtnl_message
**ret
) {
610 assert_return(nl
, -EINVAL
);
611 assert_return(!rtnl_pid_changed(nl
), -ECHILD
);
612 assert_return(message
, -EINVAL
);
614 r
= sd_rtnl_send(nl
, message
, &serial
);
618 timeout
= calc_elapse(usec
);
622 _cleanup_rtnl_message_unref_ sd_rtnl_message
*incoming
= NULL
;
627 if (nl
->rqueue_size
>= RTNL_RQUEUE_MAX
)
630 /* Make sure there's room for queueing this
631 * locally, before we read the message */
633 q
= realloc(nl
->rqueue
, (nl
->rqueue_size
+ 1) * sizeof(sd_rtnl_message
*));
641 r
= socket_read_message(nl
, &incoming
);
645 uint32_t received_serial
= rtnl_message_get_serial(incoming
);
647 if (received_serial
== serial
) {
648 r
= sd_rtnl_message_get_errno(incoming
);
660 /* Room was allocated on the queue above */
661 nl
->rqueue
[nl
->rqueue_size
++] = incoming
;
665 /* Try to read more, right away */
674 n
= now(CLOCK_MONOTONIC
);
680 left
= (uint64_t) -1;
682 r
= rtnl_poll(nl
, true, left
);
686 r
= dispatch_wqueue(nl
);
692 int sd_rtnl_flush(sd_rtnl
*rtnl
) {
695 assert_return(rtnl
, -EINVAL
);
696 assert_return(!rtnl_pid_changed(rtnl
), -ECHILD
);
698 if (rtnl
->wqueue_size
<= 0)
702 r
= dispatch_wqueue(rtnl
);
706 if (rtnl
->wqueue_size
<= 0)
709 r
= rtnl_poll(rtnl
, false, (uint64_t) -1);
715 int sd_rtnl_get_events(sd_rtnl
*rtnl
) {
718 assert_return(rtnl
, -EINVAL
);
719 assert_return(!rtnl_pid_changed(rtnl
), -ECHILD
);
721 if (rtnl
->rqueue_size
<= 0)
723 if (rtnl
->wqueue_size
> 0)
729 int sd_rtnl_get_timeout(sd_rtnl
*rtnl
, uint64_t *timeout_usec
) {
730 struct reply_callback
*c
;
732 assert_return(rtnl
, -EINVAL
);
733 assert_return(timeout_usec
, -EINVAL
);
734 assert_return(!rtnl_pid_changed(rtnl
), -ECHILD
);
736 if (rtnl
->rqueue_size
> 0) {
741 c
= prioq_peek(rtnl
->reply_callbacks_prioq
);
743 *timeout_usec
= (uint64_t) -1;
747 *timeout_usec
= c
->timeout
;
752 static int io_callback(sd_event_source
*s
, int fd
, uint32_t revents
, void *userdata
) {
753 sd_rtnl
*rtnl
= userdata
;
758 r
= sd_rtnl_process(rtnl
, NULL
);
765 static int time_callback(sd_event_source
*s
, uint64_t usec
, void *userdata
) {
766 sd_rtnl
*rtnl
= userdata
;
771 r
= sd_rtnl_process(rtnl
, NULL
);
778 static int prepare_callback(sd_event_source
*s
, void *userdata
) {
779 sd_rtnl
*rtnl
= userdata
;
786 e
= sd_rtnl_get_events(rtnl
);
790 r
= sd_event_source_set_io_events(rtnl
->io_event_source
, e
);
794 r
= sd_rtnl_get_timeout(rtnl
, &until
);
800 j
= sd_event_source_set_time(rtnl
->time_event_source
, until
);
805 r
= sd_event_source_set_enabled(rtnl
->time_event_source
, r
> 0);
812 static int exit_callback(sd_event_source
*event
, void *userdata
) {
813 sd_rtnl
*rtnl
= userdata
;
822 int sd_rtnl_attach_event(sd_rtnl
*rtnl
, sd_event
*event
, int priority
) {
825 assert_return(rtnl
, -EINVAL
);
826 assert_return(!rtnl
->event
, -EBUSY
);
828 assert(!rtnl
->io_event_source
);
829 assert(!rtnl
->time_event_source
);
832 rtnl
->event
= sd_event_ref(event
);
834 r
= sd_event_default(&rtnl
->event
);
839 r
= sd_event_add_io(rtnl
->event
, &rtnl
->io_event_source
, rtnl
->fd
, 0, io_callback
, rtnl
);
843 r
= sd_event_source_set_priority(rtnl
->io_event_source
, priority
);
847 r
= sd_event_source_set_prepare(rtnl
->io_event_source
, prepare_callback
);
851 r
= sd_event_add_time(rtnl
->event
, &rtnl
->time_event_source
, CLOCK_MONOTONIC
, 0, 0, time_callback
, rtnl
);
855 r
= sd_event_source_set_priority(rtnl
->time_event_source
, priority
);
859 r
= sd_event_add_exit(rtnl
->event
, &rtnl
->exit_event_source
, exit_callback
, rtnl
);
866 sd_rtnl_detach_event(rtnl
);
870 int sd_rtnl_detach_event(sd_rtnl
*rtnl
) {
871 assert_return(rtnl
, -EINVAL
);
872 assert_return(rtnl
->event
, -ENXIO
);
874 if (rtnl
->io_event_source
)
875 rtnl
->io_event_source
= sd_event_source_unref(rtnl
->io_event_source
);
877 if (rtnl
->time_event_source
)
878 rtnl
->time_event_source
= sd_event_source_unref(rtnl
->time_event_source
);
880 if (rtnl
->exit_event_source
)
881 rtnl
->exit_event_source
= sd_event_source_unref(rtnl
->exit_event_source
);
884 rtnl
->event
= sd_event_unref(rtnl
->event
);
889 int sd_rtnl_add_match(sd_rtnl
*rtnl
,
891 sd_rtnl_message_handler_t callback
,
893 struct match_callback
*c
;
895 assert_return(rtnl
, -EINVAL
);
896 assert_return(callback
, -EINVAL
);
897 assert_return(!rtnl_pid_changed(rtnl
), -ECHILD
);
898 assert_return(rtnl_message_type_is_link(type
) ||
899 rtnl_message_type_is_addr(type
) ||
900 rtnl_message_type_is_route(type
), -ENOTSUP
);
902 c
= new0(struct match_callback
, 1);
906 c
->callback
= callback
;
908 c
->userdata
= userdata
;
910 LIST_PREPEND(match_callbacks
, rtnl
->match_callbacks
, c
);
915 int sd_rtnl_remove_match(sd_rtnl
*rtnl
,
917 sd_rtnl_message_handler_t callback
,
919 struct match_callback
*c
;
921 assert_return(rtnl
, -EINVAL
);
922 assert_return(callback
, -EINVAL
);
923 assert_return(!rtnl_pid_changed(rtnl
), -ECHILD
);
925 LIST_FOREACH(match_callbacks
, c
, rtnl
->match_callbacks
)
926 if (c
->callback
== callback
&& c
->type
== type
&& c
->userdata
== userdata
) {
927 LIST_REMOVE(match_callbacks
, rtnl
->match_callbacks
, c
);