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