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