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