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