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