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