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