]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/libsystemd/sd-rtnl/sd-rtnl.c
refcnt: refcnt is unsigned, fix comparisons
[thirdparty/systemd.git] / src / libsystemd / sd-rtnl / sd-rtnl.c
CommitLineData
65f568bb
TG
1/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
2
3/***
4 This file is part of systemd.
5
6 Copyright 2013 Tom Gundersen <teg@jklm.no>
7
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.
12
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.
17
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/>.
20***/
21
22#include <sys/socket.h>
23#include <poll.h>
24
66269b05 25#include "missing.h"
65f568bb
TG
26#include "macro.h"
27#include "util.h"
e16bcf98 28#include "hashmap.h"
65f568bb
TG
29
30#include "sd-rtnl.h"
31#include "rtnl-internal.h"
a33dece5 32#include "rtnl-util.h"
65f568bb
TG
33
34static int sd_rtnl_new(sd_rtnl **ret) {
689703f6 35 _cleanup_rtnl_unref_ sd_rtnl *rtnl = NULL;
65f568bb
TG
36
37 assert_return(ret, -EINVAL);
38
39 rtnl = new0(sd_rtnl, 1);
40 if (!rtnl)
41 return -ENOMEM;
42
43 rtnl->n_ref = REFCNT_INIT;
44
45 rtnl->fd = -1;
46
47 rtnl->sockaddr.nl.nl_family = AF_NETLINK;
48
adf412b9
TG
49 rtnl->original_pid = getpid();
50
8cec01b9
TG
51 LIST_HEAD_INIT(rtnl->match_callbacks);
52
4555ec72
TG
53 /* We guarantee that wqueue always has space for at least
54 * one entry */
77768cba 55 if (!GREEDY_REALLOC(rtnl->wqueue, rtnl->wqueue_allocated, 1))
4555ec72 56 return -ENOMEM;
4555ec72 57
a88f77c4
TG
58 /* We guarantee that the read buffer has at least space for
59 * a message header */
60 if (!greedy_realloc((void**)&rtnl->rbuffer, &rtnl->rbuffer_allocated,
61 sizeof(struct nlmsghdr), sizeof(uint8_t)))
62 return -ENOMEM;
63
65f568bb 64 *ret = rtnl;
689703f6
TG
65 rtnl = NULL;
66
65f568bb
TG
67 return 0;
68}
69
adf412b9
TG
70static bool rtnl_pid_changed(sd_rtnl *rtnl) {
71 assert(rtnl);
72
73 /* We don't support people creating an rtnl connection and
74 * keeping it around over a fork(). Let's complain. */
75
76 return rtnl->original_pid != getpid();
77}
78
897e184c
TG
79static int rtnl_compute_groups_ap(uint32_t *_groups, unsigned n_groups, va_list ap) {
80 uint32_t groups = 0;
81 unsigned i;
82
83 for (i = 0; i < n_groups; i++) {
84 unsigned group;
85
86 group = va_arg(ap, unsigned);
87 assert_return(group < 32, -EINVAL);
88
89 groups |= group ? (1 << (group - 1)) : 0;
90 }
91
92 *_groups = groups;
93
94 return 0;
95}
96
6d0b55c2 97static int rtnl_open_fd_ap(sd_rtnl **ret, int fd, unsigned n_groups, va_list ap) {
cf6a8911 98 _cleanup_rtnl_unref_ sd_rtnl *rtnl = NULL;
d4bbdb77 99 socklen_t addrlen;
bc078e71 100 int r, one = 1;
65f568bb 101
9d0db178 102 assert_return(ret, -EINVAL);
6d0b55c2 103 assert_return(fd >= 0, -EINVAL);
9d0db178 104
65f568bb
TG
105 r = sd_rtnl_new(&rtnl);
106 if (r < 0)
107 return r;
108
6d0b55c2 109 r = setsockopt(fd, SOL_SOCKET, SO_PASSCRED, &one, sizeof(one));
66269b05
TG
110 if (r < 0)
111 return -errno;
112
6d0b55c2 113 r = setsockopt(fd, SOL_NETLINK, NETLINK_PKTINFO, &one, sizeof(one));
66269b05 114 if (r < 0)
bc078e71
TG
115 return -errno;
116
897e184c 117 r = rtnl_compute_groups_ap(&rtnl->sockaddr.nl.nl_groups, n_groups, ap);
897e184c
TG
118 if (r < 0)
119 return r;
65f568bb 120
d4bbdb77
TG
121 addrlen = sizeof(rtnl->sockaddr);
122
6d0b55c2 123 r = bind(fd, &rtnl->sockaddr.sa, addrlen);
fe5c4e49
TG
124 if (r < 0)
125 return -errno;
65f568bb 126
6d0b55c2 127 r = getsockname(fd, &rtnl->sockaddr.sa, &addrlen);
d4bbdb77
TG
128 if (r < 0)
129 return r;
130
6d0b55c2
LP
131 rtnl->fd = fd;
132
65f568bb 133 *ret = rtnl;
fe5c4e49 134 rtnl = NULL;
65f568bb
TG
135
136 return 0;
137}
138
6d0b55c2
LP
139int sd_rtnl_open_fd(sd_rtnl **ret, int fd, unsigned n_groups, ...) {
140 va_list ap;
141 int r;
142
143 va_start(ap, n_groups);
144 r = rtnl_open_fd_ap(ret, fd, n_groups, ap);
145 va_end(ap);
146
147 return r;
148}
149
150int sd_rtnl_open(sd_rtnl **ret, unsigned n_groups, ...) {
151 va_list ap;
152 int fd, r;
153
154 fd = socket(PF_NETLINK, SOCK_RAW|SOCK_CLOEXEC|SOCK_NONBLOCK, NETLINK_ROUTE);
155 if (fd < 0)
156 return -errno;
157
158 va_start(ap, n_groups);
159 r = rtnl_open_fd_ap(ret, fd, n_groups, ap);
160 va_end(ap);
161
162 if (r < 0) {
163 safe_close(fd);
164 return r;
165 }
166
167 return 0;
168}
169
be660c37
AR
170int sd_rtnl_inc_rcvbuf(const sd_rtnl *const rtnl, const int size) {
171 return fd_inc_rcvbuf(rtnl->fd, size);
172}
173
65f568bb 174sd_rtnl *sd_rtnl_ref(sd_rtnl *rtnl) {
8c578303
TG
175 assert_return(rtnl, NULL);
176 assert_return(!rtnl_pid_changed(rtnl), NULL);
177
65f568bb
TG
178 if (rtnl)
179 assert_se(REFCNT_INC(rtnl->n_ref) >= 2);
180
181 return rtnl;
182}
183
184sd_rtnl *sd_rtnl_unref(sd_rtnl *rtnl) {
22fdeadc
DH
185 if (!rtnl)
186 return NULL;
187
8c578303 188 assert_return(!rtnl_pid_changed(rtnl), NULL);
22fdeadc 189
f0c4b1c3 190 if (REFCNT_DEC(rtnl->n_ref) == 0) {
8cec01b9 191 struct match_callback *f;
4555ec72
TG
192 unsigned i;
193
8c578303
TG
194 for (i = 0; i < rtnl->rqueue_size; i++)
195 sd_rtnl_message_unref(rtnl->rqueue[i]);
196 free(rtnl->rqueue);
8cec01b9 197
4e996881
TG
198 for (i = 0; i < rtnl->rqueue_partial_size; i++)
199 sd_rtnl_message_unref(rtnl->rqueue_partial[i]);
200 free(rtnl->rqueue_partial);
201
8c578303
TG
202 for (i = 0; i < rtnl->wqueue_size; i++)
203 sd_rtnl_message_unref(rtnl->wqueue[i]);
204 free(rtnl->wqueue);
22fdeadc 205
a88f77c4
TG
206 free(rtnl->rbuffer);
207
8c578303
TG
208 hashmap_free_free(rtnl->reply_callbacks);
209 prioq_free(rtnl->reply_callbacks_prioq);
22fdeadc 210
8c578303
TG
211 sd_event_source_unref(rtnl->io_event_source);
212 sd_event_source_unref(rtnl->time_event_source);
213 sd_event_source_unref(rtnl->exit_event_source);
214 sd_event_unref(rtnl->event);
22fdeadc 215
8c578303
TG
216 while ((f = rtnl->match_callbacks)) {
217 LIST_REMOVE(match_callbacks, rtnl->match_callbacks, f);
218 free(f);
22fdeadc 219 }
65f568bb 220
8c578303
TG
221 safe_close(rtnl->fd);
222 free(rtnl);
223 }
22fdeadc 224
65f568bb
TG
225 return NULL;
226}
227
3dd215e0
TG
228static void rtnl_seal_message(sd_rtnl *rtnl, sd_rtnl_message *m) {
229 assert(rtnl);
230 assert(!rtnl_pid_changed(rtnl));
231 assert(m);
232 assert(m->hdr);
233
234 m->hdr->nlmsg_seq = rtnl->serial++;
235
236 rtnl_message_seal(m);
237
238 return;
239}
240
4555ec72
TG
241int sd_rtnl_send(sd_rtnl *nl,
242 sd_rtnl_message *message,
243 uint32_t *serial) {
244 int r;
65f568bb
TG
245
246 assert_return(nl, -EINVAL);
adf412b9 247 assert_return(!rtnl_pid_changed(nl), -ECHILD);
65f568bb 248 assert_return(message, -EINVAL);
3dd215e0 249 assert_return(!message->sealed, -EPERM);
65f568bb 250
3dd215e0 251 rtnl_seal_message(nl, message);
65f568bb 252
4555ec72
TG
253 if (nl->wqueue_size <= 0) {
254 /* send directly */
255 r = socket_write_message(nl, message);
256 if (r < 0)
257 return r;
258 else if (r == 0) {
259 /* nothing was sent, so let's put it on
260 * the queue */
261 nl->wqueue[0] = sd_rtnl_message_ref(message);
262 nl->wqueue_size = 1;
263 }
264 } else {
4555ec72 265 /* append to queue */
6190b9f9
TG
266 if (nl->wqueue_size >= RTNL_WQUEUE_MAX) {
267 log_debug("rtnl: exhausted the write queue size (%d)", RTNL_WQUEUE_MAX);
4555ec72 268 return -ENOBUFS;
6190b9f9 269 }
65f568bb 270
77768cba 271 if (!GREEDY_REALLOC(nl->wqueue, nl->wqueue_allocated, nl->wqueue_size + 1))
4555ec72 272 return -ENOMEM;
65f568bb 273
77768cba 274 nl->wqueue[nl->wqueue_size ++] = sd_rtnl_message_ref(message);
4555ec72 275 }
65f568bb 276
4555ec72 277 if (serial)
3815f36f 278 *serial = rtnl_message_get_serial(message);
65f568bb 279
4555ec72
TG
280 return 1;
281}
65f568bb 282
1b89cf56
TG
283int rtnl_rqueue_make_room(sd_rtnl *rtnl) {
284 assert(rtnl);
285
6190b9f9
TG
286 if (rtnl->rqueue_size >= RTNL_RQUEUE_MAX) {
287 log_debug("rtnl: exhausted the read queue size (%d)", RTNL_RQUEUE_MAX);
1b89cf56 288 return -ENOBUFS;
6190b9f9 289 }
1b89cf56
TG
290
291 if (!GREEDY_REALLOC(rtnl->rqueue, rtnl->rqueue_allocated, rtnl->rqueue_size + 1))
292 return -ENOMEM;
293
294 return 0;
295}
296
4e996881
TG
297int rtnl_rqueue_partial_make_room(sd_rtnl *rtnl) {
298 assert(rtnl);
299
6190b9f9
TG
300 if (rtnl->rqueue_partial_size >= RTNL_RQUEUE_MAX) {
301 log_debug("rtnl: exhausted the partial read queue size (%d)", RTNL_RQUEUE_MAX);
4e996881 302 return -ENOBUFS;
6190b9f9 303 }
4e996881
TG
304
305 if (!GREEDY_REALLOC(rtnl->rqueue_partial, rtnl->rqueue_partial_allocated,
306 rtnl->rqueue_partial_size + 1))
307 return -ENOMEM;
308
309 return 0;
310}
311
4555ec72 312static int dispatch_rqueue(sd_rtnl *rtnl, sd_rtnl_message **message) {
4555ec72 313 int r;
65f568bb 314
4555ec72
TG
315 assert(rtnl);
316 assert(message);
317
1b89cf56
TG
318 if (rtnl->rqueue_size <= 0) {
319 /* Try to read a new message */
320 r = socket_read_message(rtnl);
321 if (r <= 0)
322 return r;
4555ec72
TG
323 }
324
1b89cf56
TG
325 /* Dispatch a queued message */
326 *message = rtnl->rqueue[0];
327 rtnl->rqueue_size --;
328 memmove(rtnl->rqueue, rtnl->rqueue + 1, sizeof(sd_rtnl_message*) * rtnl->rqueue_size);
4555ec72
TG
329
330 return 1;
331}
332
333static int dispatch_wqueue(sd_rtnl *rtnl) {
334 int r, ret = 0;
335
336 assert(rtnl);
337
338 while (rtnl->wqueue_size > 0) {
339 r = socket_write_message(rtnl, rtnl->wqueue[0]);
65f568bb
TG
340 if (r < 0)
341 return r;
4555ec72
TG
342 else if (r == 0)
343 /* Didn't do anything this time */
344 return ret;
345 else {
346 /* see equivalent in sd-bus.c */
347 sd_rtnl_message_unref(rtnl->wqueue[0]);
348 rtnl->wqueue_size --;
349 memmove(rtnl->wqueue, rtnl->wqueue + 1, sizeof(sd_rtnl_message*) * rtnl->wqueue_size);
350
351 ret = 1;
65f568bb
TG
352 }
353 }
354
4555ec72
TG
355 return ret;
356}
357
e16bcf98 358static int process_timeout(sd_rtnl *rtnl) {
cf6a8911 359 _cleanup_rtnl_message_unref_ sd_rtnl_message *m = NULL;
e16bcf98
TG
360 struct reply_callback *c;
361 usec_t n;
362 int r;
363
364 assert(rtnl);
365
366 c = prioq_peek(rtnl->reply_callbacks_prioq);
367 if (!c)
368 return 0;
369
370 n = now(CLOCK_MONOTONIC);
371 if (c->timeout > n)
372 return 0;
373
3815f36f 374 r = rtnl_message_new_synthetic_error(-ETIMEDOUT, c->serial, &m);
e16bcf98
TG
375 if (r < 0)
376 return r;
377
378 assert_se(prioq_pop(rtnl->reply_callbacks_prioq) == c);
379 hashmap_remove(rtnl->reply_callbacks, &c->serial);
380
381 r = c->callback(rtnl, m, c->userdata);
382 free(c);
383
384 return r < 0 ? r : 1;
385}
386
387static int process_reply(sd_rtnl *rtnl, sd_rtnl_message *m) {
388 struct reply_callback *c;
389 uint64_t serial;
390 int r;
391
392 assert(rtnl);
393 assert(m);
394
1f0db3ed
TG
395 if (sd_rtnl_message_is_broadcast(m))
396 return 0;
397
3815f36f 398 serial = rtnl_message_get_serial(m);
e16bcf98
TG
399 c = hashmap_remove(rtnl->reply_callbacks, &serial);
400 if (!c)
401 return 0;
402
403 if (c->timeout != 0)
404 prioq_remove(rtnl->reply_callbacks_prioq, c, &c->prioq_idx);
405
406 r = c->callback(rtnl, m, c->userdata);
407 free(c);
408
409 return r;
410}
411
8cec01b9
TG
412static int process_match(sd_rtnl *rtnl, sd_rtnl_message *m) {
413 struct match_callback *c;
414 uint16_t type;
415 int r;
416
417 assert(rtnl);
418 assert(m);
419
420 r = sd_rtnl_message_get_type(m, &type);
421 if (r < 0)
422 return r;
423
424 LIST_FOREACH(match_callbacks, c, rtnl->match_callbacks) {
23a7f0f7 425 if (type == c->type) {
8cec01b9
TG
426 r = c->callback(rtnl, m, c->userdata);
427 if (r != 0)
428 return r;
429 }
430 }
431
432 return 0;
433}
434
4555ec72 435static int process_running(sd_rtnl *rtnl, sd_rtnl_message **ret) {
cf6a8911 436 _cleanup_rtnl_message_unref_ sd_rtnl_message *m = NULL;
4555ec72
TG
437 int r;
438
9d0db178
TG
439 assert(rtnl);
440
e16bcf98
TG
441 r = process_timeout(rtnl);
442 if (r != 0)
443 goto null_message;
444
4555ec72
TG
445 r = dispatch_wqueue(rtnl);
446 if (r != 0)
447 goto null_message;
448
449 r = dispatch_rqueue(rtnl, &m);
450 if (r < 0)
451 return r;
452 if (!m)
453 goto null_message;
454
e16bcf98
TG
455 r = process_reply(rtnl, m);
456 if (r != 0)
457 goto null_message;
458
8cec01b9
TG
459 r = process_match(rtnl, m);
460 if (r != 0)
461 goto null_message;
462
4555ec72
TG
463 if (ret) {
464 *ret = m;
465 m = NULL;
466
467 return 1;
468 }
469
470 return 1;
471
472null_message:
473 if (r >= 0 && ret)
474 *ret = NULL;
475
476 return r;
477}
e16bcf98 478
4555ec72 479int sd_rtnl_process(sd_rtnl *rtnl, sd_rtnl_message **ret) {
e16bcf98 480 RTNL_DONT_DESTROY(rtnl);
4555ec72
TG
481 int r;
482
483 assert_return(rtnl, -EINVAL);
484 assert_return(!rtnl_pid_changed(rtnl), -ECHILD);
485 assert_return(!rtnl->processing, -EBUSY);
486
487 rtnl->processing = true;
488 r = process_running(rtnl, ret);
489 rtnl->processing = false;
490
491 return r;
492}
493
494static usec_t calc_elapse(uint64_t usec) {
495 if (usec == (uint64_t) -1)
496 return 0;
497
498 if (usec == 0)
499 usec = RTNL_DEFAULT_TIMEOUT;
500
501 return now(CLOCK_MONOTONIC) + usec;
502}
503
b4f2a5b1 504static int rtnl_poll(sd_rtnl *rtnl, bool need_more, uint64_t timeout_usec) {
4555ec72
TG
505 struct pollfd p[1] = {};
506 struct timespec ts;
3a43da28 507 usec_t m = USEC_INFINITY;
b4f2a5b1
TG
508 int r, e;
509
510 assert(rtnl);
4555ec72 511
b4f2a5b1
TG
512 e = sd_rtnl_get_events(rtnl);
513 if (e < 0)
514 return e;
4555ec72 515
b4f2a5b1
TG
516 if (need_more)
517 /* Caller wants more data, and doesn't care about
518 * what's been read or any other timeouts. */
f55dc7c9 519 e |= POLLIN;
b4f2a5b1
TG
520 else {
521 usec_t until;
522 /* Caller wants to process if there is something to
523 * process, but doesn't care otherwise */
524
525 r = sd_rtnl_get_timeout(rtnl, &until);
526 if (r < 0)
527 return r;
528 if (r > 0) {
529 usec_t nw;
530 nw = now(CLOCK_MONOTONIC);
531 m = until > nw ? until - nw : 0;
532 }
533 }
65f568bb 534
b4f2a5b1
TG
535 if (timeout_usec != (uint64_t) -1 && (m == (uint64_t) -1 || timeout_usec < m))
536 m = timeout_usec;
537
538 p[0].fd = rtnl->fd;
539 p[0].events = e;
540
541 r = ppoll(p, 1, m == (uint64_t) -1 ? NULL : timespec_store(&ts, m), NULL);
4555ec72
TG
542 if (r < 0)
543 return -errno;
544
545 return r > 0 ? 1 : 0;
546}
547
548int sd_rtnl_wait(sd_rtnl *nl, uint64_t timeout_usec) {
549 assert_return(nl, -EINVAL);
550 assert_return(!rtnl_pid_changed(nl), -ECHILD);
551
552 if (nl->rqueue_size > 0)
553 return 0;
554
b4f2a5b1 555 return rtnl_poll(nl, false, timeout_usec);
4555ec72
TG
556}
557
e16bcf98
TG
558static int timeout_compare(const void *a, const void *b) {
559 const struct reply_callback *x = a, *y = b;
560
561 if (x->timeout != 0 && y->timeout == 0)
562 return -1;
563
564 if (x->timeout == 0 && y->timeout != 0)
565 return 1;
566
567 if (x->timeout < y->timeout)
568 return -1;
569
570 if (x->timeout > y->timeout)
571 return 1;
572
573 return 0;
574}
575
576int sd_rtnl_call_async(sd_rtnl *nl,
577 sd_rtnl_message *m,
578 sd_rtnl_message_handler_t callback,
579 void *userdata,
580 uint64_t usec,
581 uint32_t *serial) {
582 struct reply_callback *c;
583 uint32_t s;
584 int r, k;
585
586 assert_return(nl, -EINVAL);
587 assert_return(m, -EINVAL);
588 assert_return(callback, -EINVAL);
589 assert_return(!rtnl_pid_changed(nl), -ECHILD);
590
d5099efc 591 r = hashmap_ensure_allocated(&nl->reply_callbacks, &uint64_hash_ops);
e16bcf98
TG
592 if (r < 0)
593 return r;
594
595 if (usec != (uint64_t) -1) {
596 r = prioq_ensure_allocated(&nl->reply_callbacks_prioq, timeout_compare);
597 if (r < 0)
598 return r;
599 }
600
601 c = new0(struct reply_callback, 1);
602 if (!c)
603 return -ENOMEM;
604
605 c->callback = callback;
606 c->userdata = userdata;
607 c->timeout = calc_elapse(usec);
608
609 k = sd_rtnl_send(nl, m, &s);
610 if (k < 0) {
611 free(c);
612 return k;
613 }
614
615 c->serial = s;
616
617 r = hashmap_put(nl->reply_callbacks, &c->serial, c);
618 if (r < 0) {
619 free(c);
620 return r;
621 }
622
623 if (c->timeout != 0) {
624 r = prioq_put(nl->reply_callbacks_prioq, c, &c->prioq_idx);
625 if (r > 0) {
626 c->timeout = 0;
627 sd_rtnl_call_async_cancel(nl, c->serial);
628 return r;
629 }
630 }
631
632 if (serial)
633 *serial = s;
634
635 return k;
636}
637
638int sd_rtnl_call_async_cancel(sd_rtnl *nl, uint32_t serial) {
639 struct reply_callback *c;
640 uint64_t s = serial;
641
642 assert_return(nl, -EINVAL);
643 assert_return(serial != 0, -EINVAL);
644 assert_return(!rtnl_pid_changed(nl), -ECHILD);
645
646 c = hashmap_remove(nl->reply_callbacks, &s);
647 if (!c)
648 return 0;
649
650 if (c->timeout != 0)
651 prioq_remove(nl->reply_callbacks_prioq, c, &c->prioq_idx);
652
653 free(c);
654 return 1;
655}
656
1b89cf56 657int sd_rtnl_call(sd_rtnl *rtnl,
4555ec72
TG
658 sd_rtnl_message *message,
659 uint64_t usec,
660 sd_rtnl_message **ret) {
661 usec_t timeout;
662 uint32_t serial;
1b89cf56 663 unsigned i = 0;
4555ec72
TG
664 int r;
665
1b89cf56
TG
666 assert_return(rtnl, -EINVAL);
667 assert_return(!rtnl_pid_changed(rtnl), -ECHILD);
4555ec72
TG
668 assert_return(message, -EINVAL);
669
1b89cf56 670 r = sd_rtnl_send(rtnl, message, &serial);
4555ec72
TG
671 if (r < 0)
672 return r;
673
674 timeout = calc_elapse(usec);
675
65f568bb 676 for (;;) {
4555ec72 677 usec_t left;
65f568bb 678
1b89cf56
TG
679 while (i < rtnl->rqueue_size) {
680 sd_rtnl_message *incoming;
681 uint32_t received_serial;
65f568bb 682
1b89cf56
TG
683 incoming = rtnl->rqueue[i];
684 received_serial = rtnl_message_get_serial(incoming);
65f568bb
TG
685
686 if (received_serial == serial) {
1b89cf56
TG
687 /* found a match, remove from rqueue and return it */
688 memmove(rtnl->rqueue + i,rtnl->rqueue + i + 1,
689 sizeof(sd_rtnl_message*) * (rtnl->rqueue_size - i - 1));
690 rtnl->rqueue_size--;
691
e16bcf98 692 r = sd_rtnl_message_get_errno(incoming);
1b89cf56
TG
693 if (r < 0) {
694 sd_rtnl_message_unref(incoming);
65f568bb 695 return r;
1b89cf56 696 }
65f568bb 697
fe5c4e49 698 if (ret) {
4555ec72 699 *ret = incoming;
1b89cf56
TG
700 } else
701 sd_rtnl_message_unref(incoming);
65f568bb 702
4555ec72 703 return 1;
65f568bb 704 }
4555ec72 705
4555ec72 706 /* Try to read more, right away */
1b89cf56 707 i ++;
65f568bb 708 }
1b89cf56
TG
709
710 r = socket_read_message(rtnl);
711 if (r < 0)
712 return r;
713 if (r > 0)
6ff8806e 714 /* received message, so try to process straight away */
4555ec72 715 continue;
65f568bb 716
4555ec72
TG
717 if (timeout > 0) {
718 usec_t n;
719
720 n = now(CLOCK_MONOTONIC);
721 if (n >= timeout)
722 return -ETIMEDOUT;
723
724 left = timeout - n;
725 } else
726 left = (uint64_t) -1;
727
1b89cf56 728 r = rtnl_poll(rtnl, true, left);
4555ec72
TG
729 if (r < 0)
730 return r;
b551ddd3
TG
731 else if (r == 0)
732 return -ETIMEDOUT;
4555ec72 733
1b89cf56 734 r = dispatch_wqueue(rtnl);
4555ec72
TG
735 if (r < 0)
736 return r;
737 }
65f568bb 738}
b4f2a5b1
TG
739
740int sd_rtnl_flush(sd_rtnl *rtnl) {
741 int r;
742
743 assert_return(rtnl, -EINVAL);
744 assert_return(!rtnl_pid_changed(rtnl), -ECHILD);
745
746 if (rtnl->wqueue_size <= 0)
747 return 0;
748
749 for (;;) {
750 r = dispatch_wqueue(rtnl);
751 if (r < 0)
752 return r;
753
754 if (rtnl->wqueue_size <= 0)
755 return 0;
756
757 r = rtnl_poll(rtnl, false, (uint64_t) -1);
758 if (r < 0)
759 return r;
760 }
761}
762
763int sd_rtnl_get_events(sd_rtnl *rtnl) {
764 int flags = 0;
765
766 assert_return(rtnl, -EINVAL);
767 assert_return(!rtnl_pid_changed(rtnl), -ECHILD);
768
769 if (rtnl->rqueue_size <= 0)
770 flags |= POLLIN;
771 if (rtnl->wqueue_size > 0)
772 flags |= POLLOUT;
773
774 return flags;
775}
776
777int sd_rtnl_get_timeout(sd_rtnl *rtnl, uint64_t *timeout_usec) {
778 struct reply_callback *c;
779
780 assert_return(rtnl, -EINVAL);
781 assert_return(timeout_usec, -EINVAL);
782 assert_return(!rtnl_pid_changed(rtnl), -ECHILD);
783
784 if (rtnl->rqueue_size > 0) {
785 *timeout_usec = 0;
786 return 1;
787 }
788
789 c = prioq_peek(rtnl->reply_callbacks_prioq);
790 if (!c) {
791 *timeout_usec = (uint64_t) -1;
792 return 0;
793 }
794
795 *timeout_usec = c->timeout;
796
797 return 1;
798}
799
800static int io_callback(sd_event_source *s, int fd, uint32_t revents, void *userdata) {
801 sd_rtnl *rtnl = userdata;
802 int r;
803
804 assert(rtnl);
805
806 r = sd_rtnl_process(rtnl, NULL);
807 if (r < 0)
808 return r;
809
810 return 1;
811}
812
813static int time_callback(sd_event_source *s, uint64_t usec, void *userdata) {
814 sd_rtnl *rtnl = userdata;
815 int r;
816
817 assert(rtnl);
818
819 r = sd_rtnl_process(rtnl, NULL);
820 if (r < 0)
821 return r;
822
823 return 1;
824}
825
826static int prepare_callback(sd_event_source *s, void *userdata) {
827 sd_rtnl *rtnl = userdata;
828 int r, e;
829 usec_t until;
830
831 assert(s);
832 assert(rtnl);
833
834 e = sd_rtnl_get_events(rtnl);
835 if (e < 0)
836 return e;
837
838 r = sd_event_source_set_io_events(rtnl->io_event_source, e);
839 if (r < 0)
840 return r;
841
842 r = sd_rtnl_get_timeout(rtnl, &until);
843 if (r < 0)
844 return r;
845 if (r > 0) {
846 int j;
847
848 j = sd_event_source_set_time(rtnl->time_event_source, until);
849 if (j < 0)
850 return j;
851 }
852
853 r = sd_event_source_set_enabled(rtnl->time_event_source, r > 0);
854 if (r < 0)
855 return r;
856
857 return 1;
858}
859
6203e07a 860static int exit_callback(sd_event_source *event, void *userdata) {
b4f2a5b1
TG
861 sd_rtnl *rtnl = userdata;
862
863 assert(event);
864
865 sd_rtnl_flush(rtnl);
866
867 return 1;
868}
869
870int sd_rtnl_attach_event(sd_rtnl *rtnl, sd_event *event, int priority) {
871 int r;
872
873 assert_return(rtnl, -EINVAL);
874 assert_return(!rtnl->event, -EBUSY);
875
876 assert(!rtnl->io_event_source);
877 assert(!rtnl->time_event_source);
878
879 if (event)
880 rtnl->event = sd_event_ref(event);
881 else {
882 r = sd_event_default(&rtnl->event);
883 if (r < 0)
884 return r;
885 }
886
151b9b96 887 r = sd_event_add_io(rtnl->event, &rtnl->io_event_source, rtnl->fd, 0, io_callback, rtnl);
b4f2a5b1
TG
888 if (r < 0)
889 goto fail;
890
891 r = sd_event_source_set_priority(rtnl->io_event_source, priority);
892 if (r < 0)
893 goto fail;
894
356779df 895 r = sd_event_source_set_description(rtnl->io_event_source, "rtnl-receive-message");
9021bb9f
TG
896 if (r < 0)
897 goto fail;
898
b4f2a5b1
TG
899 r = sd_event_source_set_prepare(rtnl->io_event_source, prepare_callback);
900 if (r < 0)
901 goto fail;
902
6a0f1f6d 903 r = sd_event_add_time(rtnl->event, &rtnl->time_event_source, CLOCK_MONOTONIC, 0, 0, time_callback, rtnl);
b4f2a5b1
TG
904 if (r < 0)
905 goto fail;
906
907 r = sd_event_source_set_priority(rtnl->time_event_source, priority);
908 if (r < 0)
909 goto fail;
910
356779df 911 r = sd_event_source_set_description(rtnl->time_event_source, "rtnl-timer");
9021bb9f
TG
912 if (r < 0)
913 goto fail;
914
151b9b96 915 r = sd_event_add_exit(rtnl->event, &rtnl->exit_event_source, exit_callback, rtnl);
b4f2a5b1
TG
916 if (r < 0)
917 goto fail;
918
356779df 919 r = sd_event_source_set_description(rtnl->exit_event_source, "rtnl-exit");
9021bb9f
TG
920 if (r < 0)
921 goto fail;
922
b4f2a5b1
TG
923 return 0;
924
925fail:
926 sd_rtnl_detach_event(rtnl);
927 return r;
928}
929
930int sd_rtnl_detach_event(sd_rtnl *rtnl) {
931 assert_return(rtnl, -EINVAL);
932 assert_return(rtnl->event, -ENXIO);
933
934 if (rtnl->io_event_source)
935 rtnl->io_event_source = sd_event_source_unref(rtnl->io_event_source);
936
937 if (rtnl->time_event_source)
938 rtnl->time_event_source = sd_event_source_unref(rtnl->time_event_source);
939
6203e07a
LP
940 if (rtnl->exit_event_source)
941 rtnl->exit_event_source = sd_event_source_unref(rtnl->exit_event_source);
b4f2a5b1
TG
942
943 if (rtnl->event)
944 rtnl->event = sd_event_unref(rtnl->event);
945
946 return 0;
947}
8cec01b9
TG
948
949int sd_rtnl_add_match(sd_rtnl *rtnl,
23a7f0f7 950 uint16_t type,
8cec01b9
TG
951 sd_rtnl_message_handler_t callback,
952 void *userdata) {
953 struct match_callback *c;
954
955 assert_return(rtnl, -EINVAL);
956 assert_return(callback, -EINVAL);
8cec01b9 957 assert_return(!rtnl_pid_changed(rtnl), -ECHILD);
3815f36f
TG
958 assert_return(rtnl_message_type_is_link(type) ||
959 rtnl_message_type_is_addr(type) ||
960 rtnl_message_type_is_route(type), -ENOTSUP);
8cec01b9
TG
961
962 c = new0(struct match_callback, 1);
963 if (!c)
964 return -ENOMEM;
965
966 c->callback = callback;
23a7f0f7 967 c->type = type;
8cec01b9
TG
968 c->userdata = userdata;
969
970 LIST_PREPEND(match_callbacks, rtnl->match_callbacks, c);
971
972 return 0;
973}
974
975int sd_rtnl_remove_match(sd_rtnl *rtnl,
23a7f0f7 976 uint16_t type,
8cec01b9
TG
977 sd_rtnl_message_handler_t callback,
978 void *userdata) {
979 struct match_callback *c;
980
981 assert_return(rtnl, -EINVAL);
982 assert_return(callback, -EINVAL);
983 assert_return(!rtnl_pid_changed(rtnl), -ECHILD);
984
985 LIST_FOREACH(match_callbacks, c, rtnl->match_callbacks)
23a7f0f7 986 if (c->callback == callback && c->type == type && c->userdata == userdata) {
8cec01b9
TG
987 LIST_REMOVE(match_callbacks, rtnl->match_callbacks, c);
988 free(c);
989
990 return 1;
991 }
992
993 return 0;
994}