]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/libsystemd/sd-netlink/sd-netlink.c
hwdb: Map 45 to bookmarks on the ThinkPad L380
[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
493 if (x->timeout < y->timeout)
494 return -1;
495
496 if (x->timeout > y->timeout)
497 return 1;
498
499 return 0;
500}
501
1c4baffc
TG
502int sd_netlink_call_async(sd_netlink *nl,
503 sd_netlink_message *m,
504 sd_netlink_message_handler_t callback,
e16bcf98
TG
505 void *userdata,
506 uint64_t usec,
507 uint32_t *serial) {
508 struct reply_callback *c;
509 uint32_t s;
510 int r, k;
511
512 assert_return(nl, -EINVAL);
513 assert_return(m, -EINVAL);
514 assert_return(callback, -EINVAL);
515 assert_return(!rtnl_pid_changed(nl), -ECHILD);
516
d5099efc 517 r = hashmap_ensure_allocated(&nl->reply_callbacks, &uint64_hash_ops);
e16bcf98
TG
518 if (r < 0)
519 return r;
520
521 if (usec != (uint64_t) -1) {
522 r = prioq_ensure_allocated(&nl->reply_callbacks_prioq, timeout_compare);
523 if (r < 0)
524 return r;
525 }
526
527 c = new0(struct reply_callback, 1);
528 if (!c)
529 return -ENOMEM;
530
531 c->callback = callback;
532 c->userdata = userdata;
533 c->timeout = calc_elapse(usec);
534
1c4baffc 535 k = sd_netlink_send(nl, m, &s);
e16bcf98
TG
536 if (k < 0) {
537 free(c);
538 return k;
539 }
540
541 c->serial = s;
542
543 r = hashmap_put(nl->reply_callbacks, &c->serial, c);
544 if (r < 0) {
545 free(c);
546 return r;
547 }
548
549 if (c->timeout != 0) {
550 r = prioq_put(nl->reply_callbacks_prioq, c, &c->prioq_idx);
551 if (r > 0) {
552 c->timeout = 0;
1c4baffc 553 sd_netlink_call_async_cancel(nl, c->serial);
e16bcf98
TG
554 return r;
555 }
556 }
557
558 if (serial)
559 *serial = s;
560
561 return k;
562}
563
1c4baffc 564int sd_netlink_call_async_cancel(sd_netlink *nl, uint32_t serial) {
e16bcf98
TG
565 struct reply_callback *c;
566 uint64_t s = serial;
567
568 assert_return(nl, -EINVAL);
569 assert_return(serial != 0, -EINVAL);
570 assert_return(!rtnl_pid_changed(nl), -ECHILD);
571
572 c = hashmap_remove(nl->reply_callbacks, &s);
573 if (!c)
574 return 0;
575
576 if (c->timeout != 0)
577 prioq_remove(nl->reply_callbacks_prioq, c, &c->prioq_idx);
578
579 free(c);
580 return 1;
581}
582
1c4baffc
TG
583int sd_netlink_call(sd_netlink *rtnl,
584 sd_netlink_message *message,
4555ec72 585 uint64_t usec,
1c4baffc 586 sd_netlink_message **ret) {
4555ec72
TG
587 usec_t timeout;
588 uint32_t serial;
4555ec72
TG
589 int r;
590
1b89cf56
TG
591 assert_return(rtnl, -EINVAL);
592 assert_return(!rtnl_pid_changed(rtnl), -ECHILD);
4555ec72
TG
593 assert_return(message, -EINVAL);
594
1c4baffc 595 r = sd_netlink_send(rtnl, message, &serial);
4555ec72
TG
596 if (r < 0)
597 return r;
598
599 timeout = calc_elapse(usec);
600
65f568bb 601 for (;;) {
4555ec72 602 usec_t left;
ea342a99 603 unsigned i;
65f568bb 604
ea342a99 605 for (i = 0; i < rtnl->rqueue_size; i++) {
1b89cf56 606 uint32_t received_serial;
65f568bb 607
ea342a99 608 received_serial = rtnl_message_get_serial(rtnl->rqueue[i]);
65f568bb
TG
609
610 if (received_serial == serial) {
4afd3348 611 _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *incoming = NULL;
ea342a99
AR
612 uint16_t type;
613
614 incoming = rtnl->rqueue[i];
615
1b89cf56
TG
616 /* found a match, remove from rqueue and return it */
617 memmove(rtnl->rqueue + i,rtnl->rqueue + i + 1,
1c4baffc 618 sizeof(sd_netlink_message*) * (rtnl->rqueue_size - i - 1));
1b89cf56
TG
619 rtnl->rqueue_size--;
620
1c4baffc 621 r = sd_netlink_message_get_errno(incoming);
ea342a99
AR
622 if (r < 0)
623 return r;
624
1c4baffc 625 r = sd_netlink_message_get_type(incoming, &type);
ea342a99 626 if (r < 0)
65f568bb 627 return r;
ea342a99
AR
628
629 if (type == NLMSG_DONE) {
630 *ret = NULL;
631 return 0;
1b89cf56 632 }
65f568bb 633
1cc6c93a
YW
634 if (ret)
635 *ret = TAKE_PTR(incoming);
65f568bb 636
4555ec72 637 return 1;
65f568bb 638 }
65f568bb 639 }
1b89cf56
TG
640
641 r = socket_read_message(rtnl);
642 if (r < 0)
643 return r;
644 if (r > 0)
6ff8806e 645 /* received message, so try to process straight away */
4555ec72 646 continue;
65f568bb 647
4555ec72
TG
648 if (timeout > 0) {
649 usec_t n;
650
651 n = now(CLOCK_MONOTONIC);
652 if (n >= timeout)
653 return -ETIMEDOUT;
654
655 left = timeout - n;
656 } else
657 left = (uint64_t) -1;
658
1b89cf56 659 r = rtnl_poll(rtnl, true, left);
4555ec72
TG
660 if (r < 0)
661 return r;
b551ddd3
TG
662 else if (r == 0)
663 return -ETIMEDOUT;
b4f2a5b1
TG
664 }
665}
666
1c4baffc 667int sd_netlink_get_events(sd_netlink *rtnl) {
b4f2a5b1
TG
668 assert_return(rtnl, -EINVAL);
669 assert_return(!rtnl_pid_changed(rtnl), -ECHILD);
670
bbe181b4
TG
671 if (rtnl->rqueue_size == 0)
672 return POLLIN;
673 else
674 return 0;
b4f2a5b1
TG
675}
676
1c4baffc 677int sd_netlink_get_timeout(sd_netlink *rtnl, uint64_t *timeout_usec) {
b4f2a5b1
TG
678 struct reply_callback *c;
679
680 assert_return(rtnl, -EINVAL);
681 assert_return(timeout_usec, -EINVAL);
682 assert_return(!rtnl_pid_changed(rtnl), -ECHILD);
683
684 if (rtnl->rqueue_size > 0) {
685 *timeout_usec = 0;
686 return 1;
687 }
688
689 c = prioq_peek(rtnl->reply_callbacks_prioq);
690 if (!c) {
691 *timeout_usec = (uint64_t) -1;
692 return 0;
693 }
694
695 *timeout_usec = c->timeout;
696
697 return 1;
698}
699
700static int io_callback(sd_event_source *s, int fd, uint32_t revents, void *userdata) {
1c4baffc 701 sd_netlink *rtnl = userdata;
b4f2a5b1
TG
702 int r;
703
704 assert(rtnl);
705
1c4baffc 706 r = sd_netlink_process(rtnl, NULL);
b4f2a5b1
TG
707 if (r < 0)
708 return r;
709
710 return 1;
711}
712
713static int time_callback(sd_event_source *s, uint64_t usec, void *userdata) {
1c4baffc 714 sd_netlink *rtnl = userdata;
b4f2a5b1
TG
715 int r;
716
717 assert(rtnl);
718
1c4baffc 719 r = sd_netlink_process(rtnl, NULL);
b4f2a5b1
TG
720 if (r < 0)
721 return r;
722
723 return 1;
724}
725
726static int prepare_callback(sd_event_source *s, void *userdata) {
1c4baffc 727 sd_netlink *rtnl = userdata;
b4f2a5b1
TG
728 int r, e;
729 usec_t until;
730
731 assert(s);
732 assert(rtnl);
733
1c4baffc 734 e = sd_netlink_get_events(rtnl);
b4f2a5b1
TG
735 if (e < 0)
736 return e;
737
738 r = sd_event_source_set_io_events(rtnl->io_event_source, e);
739 if (r < 0)
740 return r;
741
1c4baffc 742 r = sd_netlink_get_timeout(rtnl, &until);
b4f2a5b1
TG
743 if (r < 0)
744 return r;
745 if (r > 0) {
746 int j;
747
748 j = sd_event_source_set_time(rtnl->time_event_source, until);
749 if (j < 0)
750 return j;
751 }
752
753 r = sd_event_source_set_enabled(rtnl->time_event_source, r > 0);
754 if (r < 0)
755 return r;
756
757 return 1;
758}
759
32d20645 760int sd_netlink_attach_event(sd_netlink *rtnl, sd_event *event, int64_t priority) {
b4f2a5b1
TG
761 int r;
762
763 assert_return(rtnl, -EINVAL);
764 assert_return(!rtnl->event, -EBUSY);
765
766 assert(!rtnl->io_event_source);
767 assert(!rtnl->time_event_source);
768
769 if (event)
770 rtnl->event = sd_event_ref(event);
771 else {
772 r = sd_event_default(&rtnl->event);
773 if (r < 0)
774 return r;
775 }
776
151b9b96 777 r = sd_event_add_io(rtnl->event, &rtnl->io_event_source, rtnl->fd, 0, io_callback, rtnl);
b4f2a5b1
TG
778 if (r < 0)
779 goto fail;
780
781 r = sd_event_source_set_priority(rtnl->io_event_source, priority);
782 if (r < 0)
783 goto fail;
784
356779df 785 r = sd_event_source_set_description(rtnl->io_event_source, "rtnl-receive-message");
9021bb9f
TG
786 if (r < 0)
787 goto fail;
788
b4f2a5b1
TG
789 r = sd_event_source_set_prepare(rtnl->io_event_source, prepare_callback);
790 if (r < 0)
791 goto fail;
792
6a0f1f6d 793 r = sd_event_add_time(rtnl->event, &rtnl->time_event_source, CLOCK_MONOTONIC, 0, 0, time_callback, rtnl);
b4f2a5b1
TG
794 if (r < 0)
795 goto fail;
796
797 r = sd_event_source_set_priority(rtnl->time_event_source, priority);
798 if (r < 0)
799 goto fail;
800
356779df 801 r = sd_event_source_set_description(rtnl->time_event_source, "rtnl-timer");
9021bb9f
TG
802 if (r < 0)
803 goto fail;
804
b4f2a5b1
TG
805 return 0;
806
807fail:
1c4baffc 808 sd_netlink_detach_event(rtnl);
b4f2a5b1
TG
809 return r;
810}
811
1c4baffc 812int sd_netlink_detach_event(sd_netlink *rtnl) {
b4f2a5b1
TG
813 assert_return(rtnl, -EINVAL);
814 assert_return(rtnl->event, -ENXIO);
815
bbe181b4 816 rtnl->io_event_source = sd_event_source_unref(rtnl->io_event_source);
b4f2a5b1 817
bbe181b4 818 rtnl->time_event_source = sd_event_source_unref(rtnl->time_event_source);
b4f2a5b1 819
bbe181b4 820 rtnl->event = sd_event_unref(rtnl->event);
b4f2a5b1
TG
821
822 return 0;
823}
8cec01b9 824
1c4baffc 825int sd_netlink_add_match(sd_netlink *rtnl,
23a7f0f7 826 uint16_t type,
1c4baffc 827 sd_netlink_message_handler_t callback,
8cec01b9 828 void *userdata) {
31710be5
TG
829 _cleanup_free_ struct match_callback *c = NULL;
830 int r;
8cec01b9
TG
831
832 assert_return(rtnl, -EINVAL);
833 assert_return(callback, -EINVAL);
8cec01b9
TG
834 assert_return(!rtnl_pid_changed(rtnl), -ECHILD);
835
836 c = new0(struct match_callback, 1);
837 if (!c)
838 return -ENOMEM;
839
840 c->callback = callback;
23a7f0f7 841 c->type = type;
8cec01b9
TG
842 c->userdata = userdata;
843
31710be5
TG
844 switch (type) {
845 case RTM_NEWLINK:
31710be5 846 case RTM_DELLINK:
9c5a882b 847 r = socket_broadcast_group_ref(rtnl, RTNLGRP_LINK);
31710be5
TG
848 if (r < 0)
849 return r;
850
851 break;
852 case RTM_NEWADDR:
31710be5 853 case RTM_DELADDR:
9c5a882b 854 r = socket_broadcast_group_ref(rtnl, RTNLGRP_IPV4_IFADDR);
31710be5
TG
855 if (r < 0)
856 return r;
857
9c5a882b 858 r = socket_broadcast_group_ref(rtnl, RTNLGRP_IPV6_IFADDR);
31710be5
TG
859 if (r < 0)
860 return r;
861
862 break;
87e4c847
TG
863 case RTM_NEWROUTE:
864 case RTM_DELROUTE:
9c5a882b 865 r = socket_broadcast_group_ref(rtnl, RTNLGRP_IPV4_ROUTE);
87e4c847
TG
866 if (r < 0)
867 return r;
868
9c5a882b 869 r = socket_broadcast_group_ref(rtnl, RTNLGRP_IPV6_ROUTE);
87e4c847
TG
870 if (r < 0)
871 return r;
872 break;
bce67bbe
SS
873 case RTM_NEWRULE:
874 case RTM_DELRULE:
875 r = socket_broadcast_group_ref(rtnl, RTNLGRP_IPV4_RULE);
876 if (r < 0)
877 return r;
878
879 r = socket_broadcast_group_ref(rtnl, RTNLGRP_IPV6_RULE);
880 if (r < 0)
881 return r;
882 break;
31710be5
TG
883 default:
884 return -EOPNOTSUPP;
885 }
886
8cec01b9
TG
887 LIST_PREPEND(match_callbacks, rtnl->match_callbacks, c);
888
31710be5
TG
889 c = NULL;
890
8cec01b9
TG
891 return 0;
892}
893
1c4baffc 894int sd_netlink_remove_match(sd_netlink *rtnl,
23a7f0f7 895 uint16_t type,
1c4baffc 896 sd_netlink_message_handler_t callback,
8cec01b9
TG
897 void *userdata) {
898 struct match_callback *c;
9c5a882b 899 int r;
8cec01b9
TG
900
901 assert_return(rtnl, -EINVAL);
902 assert_return(callback, -EINVAL);
903 assert_return(!rtnl_pid_changed(rtnl), -ECHILD);
904
905 LIST_FOREACH(match_callbacks, c, rtnl->match_callbacks)
23a7f0f7 906 if (c->callback == callback && c->type == type && c->userdata == userdata) {
8cec01b9
TG
907 LIST_REMOVE(match_callbacks, rtnl->match_callbacks, c);
908 free(c);
909
9c5a882b
TG
910 switch (type) {
911 case RTM_NEWLINK:
912 case RTM_DELLINK:
913 r = socket_broadcast_group_unref(rtnl, RTNLGRP_LINK);
914 if (r < 0)
915 return r;
916
917 break;
918 case RTM_NEWADDR:
919 case RTM_DELADDR:
920 r = socket_broadcast_group_unref(rtnl, RTNLGRP_IPV4_IFADDR);
921 if (r < 0)
922 return r;
923
924 r = socket_broadcast_group_unref(rtnl, RTNLGRP_IPV6_IFADDR);
925 if (r < 0)
926 return r;
927
928 break;
929 case RTM_NEWROUTE:
930 case RTM_DELROUTE:
931 r = socket_broadcast_group_unref(rtnl, RTNLGRP_IPV4_ROUTE);
932 if (r < 0)
933 return r;
934
935 r = socket_broadcast_group_unref(rtnl, RTNLGRP_IPV6_ROUTE);
936 if (r < 0)
937 return r;
938 break;
939 default:
940 return -EOPNOTSUPP;
941 }
942
8cec01b9
TG
943 return 1;
944 }
945
946 return 0;
947}