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