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