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