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