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