]>
Commit | Line | Data |
---|---|---|
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 | ||
22 | #include <sys/socket.h> | |
23 | #include <poll.h> | |
24 | ||
25 | #include "macro.h" | |
26 | #include "util.h" | |
e16bcf98 | 27 | #include "hashmap.h" |
65f568bb TG |
28 | |
29 | #include "sd-rtnl.h" | |
30 | #include "rtnl-internal.h" | |
a33dece5 | 31 | #include "rtnl-util.h" |
65f568bb TG |
32 | |
33 | static int sd_rtnl_new(sd_rtnl **ret) { | |
34 | sd_rtnl *rtnl; | |
35 | ||
36 | assert_return(ret, -EINVAL); | |
37 | ||
38 | rtnl = new0(sd_rtnl, 1); | |
39 | if (!rtnl) | |
40 | return -ENOMEM; | |
41 | ||
42 | rtnl->n_ref = REFCNT_INIT; | |
43 | ||
44 | rtnl->fd = -1; | |
45 | ||
46 | rtnl->sockaddr.nl.nl_family = AF_NETLINK; | |
47 | ||
adf412b9 TG |
48 | rtnl->original_pid = getpid(); |
49 | ||
4555ec72 TG |
50 | /* We guarantee that wqueue always has space for at least |
51 | * one entry */ | |
52 | rtnl->wqueue = new(sd_rtnl_message*, 1); | |
53 | if (!rtnl->wqueue) { | |
54 | free(rtnl); | |
55 | return -ENOMEM; | |
56 | } | |
57 | ||
65f568bb TG |
58 | *ret = rtnl; |
59 | return 0; | |
60 | } | |
61 | ||
adf412b9 TG |
62 | static bool rtnl_pid_changed(sd_rtnl *rtnl) { |
63 | assert(rtnl); | |
64 | ||
65 | /* We don't support people creating an rtnl connection and | |
66 | * keeping it around over a fork(). Let's complain. */ | |
67 | ||
68 | return rtnl->original_pid != getpid(); | |
69 | } | |
70 | ||
dabfa9d1 | 71 | int sd_rtnl_open(uint32_t groups, sd_rtnl **ret) { |
fe5c4e49 | 72 | _cleanup_sd_rtnl_unref_ sd_rtnl *rtnl = NULL; |
d4bbdb77 | 73 | socklen_t addrlen; |
65f568bb TG |
74 | int r; |
75 | ||
76 | r = sd_rtnl_new(&rtnl); | |
77 | if (r < 0) | |
78 | return r; | |
79 | ||
80 | rtnl->fd = socket(PF_NETLINK, SOCK_RAW|SOCK_CLOEXEC|SOCK_NONBLOCK, NETLINK_ROUTE); | |
fe5c4e49 TG |
81 | if (rtnl->fd < 0) |
82 | return -errno; | |
65f568bb TG |
83 | |
84 | rtnl->sockaddr.nl.nl_groups = groups; | |
85 | ||
d4bbdb77 TG |
86 | addrlen = sizeof(rtnl->sockaddr); |
87 | ||
88 | r = bind(rtnl->fd, &rtnl->sockaddr.sa, addrlen); | |
fe5c4e49 TG |
89 | if (r < 0) |
90 | return -errno; | |
65f568bb | 91 | |
d4bbdb77 TG |
92 | r = getsockname(rtnl->fd, &rtnl->sockaddr.sa, &addrlen); |
93 | if (r < 0) | |
94 | return r; | |
95 | ||
65f568bb | 96 | *ret = rtnl; |
fe5c4e49 | 97 | rtnl = NULL; |
65f568bb TG |
98 | |
99 | return 0; | |
100 | } | |
101 | ||
102 | sd_rtnl *sd_rtnl_ref(sd_rtnl *rtnl) { | |
103 | if (rtnl) | |
104 | assert_se(REFCNT_INC(rtnl->n_ref) >= 2); | |
105 | ||
106 | return rtnl; | |
107 | } | |
108 | ||
109 | sd_rtnl *sd_rtnl_unref(sd_rtnl *rtnl) { | |
4555ec72 | 110 | |
65f568bb | 111 | if (rtnl && REFCNT_DEC(rtnl->n_ref) <= 0) { |
4555ec72 TG |
112 | unsigned i; |
113 | ||
114 | for (i = 0; i < rtnl->rqueue_size; i++) | |
115 | sd_rtnl_message_unref(rtnl->rqueue[i]); | |
116 | free(rtnl->rqueue); | |
117 | ||
118 | for (i = 0; i < rtnl->wqueue_size; i++) | |
119 | sd_rtnl_message_unref(rtnl->wqueue[i]); | |
120 | free(rtnl->wqueue); | |
121 | ||
e16bcf98 TG |
122 | hashmap_free_free(rtnl->reply_callbacks); |
123 | prioq_free(rtnl->reply_callbacks_prioq); | |
124 | ||
65f568bb TG |
125 | if (rtnl->fd >= 0) |
126 | close_nointr_nofail(rtnl->fd); | |
4555ec72 | 127 | |
65f568bb TG |
128 | free(rtnl); |
129 | } | |
130 | ||
131 | return NULL; | |
132 | } | |
133 | ||
4555ec72 TG |
134 | int sd_rtnl_send(sd_rtnl *nl, |
135 | sd_rtnl_message *message, | |
136 | uint32_t *serial) { | |
137 | int r; | |
65f568bb TG |
138 | |
139 | assert_return(nl, -EINVAL); | |
adf412b9 | 140 | assert_return(!rtnl_pid_changed(nl), -ECHILD); |
65f568bb TG |
141 | assert_return(message, -EINVAL); |
142 | ||
143 | r = message_seal(nl, message); | |
144 | if (r < 0) | |
145 | return r; | |
146 | ||
4555ec72 TG |
147 | if (nl->wqueue_size <= 0) { |
148 | /* send directly */ | |
149 | r = socket_write_message(nl, message); | |
150 | if (r < 0) | |
151 | return r; | |
152 | else if (r == 0) { | |
153 | /* nothing was sent, so let's put it on | |
154 | * the queue */ | |
155 | nl->wqueue[0] = sd_rtnl_message_ref(message); | |
156 | nl->wqueue_size = 1; | |
157 | } | |
158 | } else { | |
159 | sd_rtnl_message **q; | |
65f568bb | 160 | |
4555ec72 TG |
161 | /* append to queue */ |
162 | if (nl->wqueue_size >= RTNL_WQUEUE_MAX) | |
163 | return -ENOBUFS; | |
65f568bb | 164 | |
4555ec72 TG |
165 | q = realloc(nl->wqueue, sizeof(sd_rtnl_message*) * (nl->wqueue_size + 1)); |
166 | if (!q) | |
167 | return -ENOMEM; | |
65f568bb | 168 | |
4555ec72 TG |
169 | nl->wqueue = q; |
170 | q[nl->wqueue_size ++] = sd_rtnl_message_ref(message); | |
171 | } | |
65f568bb | 172 | |
4555ec72 TG |
173 | if (serial) |
174 | *serial = message_get_serial(message); | |
65f568bb | 175 | |
4555ec72 TG |
176 | return 1; |
177 | } | |
65f568bb | 178 | |
4555ec72 TG |
179 | static int dispatch_rqueue(sd_rtnl *rtnl, sd_rtnl_message **message) { |
180 | sd_rtnl_message *z = NULL; | |
181 | int r; | |
65f568bb | 182 | |
4555ec72 TG |
183 | assert(rtnl); |
184 | assert(message); | |
185 | ||
186 | if (rtnl->rqueue_size > 0) { | |
187 | /* Dispatch a queued message */ | |
188 | ||
189 | *message = rtnl->rqueue[0]; | |
190 | rtnl->rqueue_size --; | |
191 | memmove(rtnl->rqueue, rtnl->rqueue + 1, sizeof(sd_rtnl_message*) * rtnl->rqueue_size); | |
192 | ||
193 | return 1; | |
194 | } | |
195 | ||
196 | /* Try to read a new message */ | |
197 | r = socket_read_message(rtnl, &z); | |
198 | if (r < 0) | |
199 | return r; | |
200 | if (r == 0) | |
201 | return 0; | |
202 | ||
203 | *message = z; | |
204 | ||
205 | return 1; | |
206 | } | |
207 | ||
208 | static int dispatch_wqueue(sd_rtnl *rtnl) { | |
209 | int r, ret = 0; | |
210 | ||
211 | assert(rtnl); | |
212 | ||
213 | while (rtnl->wqueue_size > 0) { | |
214 | r = socket_write_message(rtnl, rtnl->wqueue[0]); | |
65f568bb TG |
215 | if (r < 0) |
216 | return r; | |
4555ec72 TG |
217 | else if (r == 0) |
218 | /* Didn't do anything this time */ | |
219 | return ret; | |
220 | else { | |
221 | /* see equivalent in sd-bus.c */ | |
222 | sd_rtnl_message_unref(rtnl->wqueue[0]); | |
223 | rtnl->wqueue_size --; | |
224 | memmove(rtnl->wqueue, rtnl->wqueue + 1, sizeof(sd_rtnl_message*) * rtnl->wqueue_size); | |
225 | ||
226 | ret = 1; | |
65f568bb TG |
227 | } |
228 | } | |
229 | ||
4555ec72 TG |
230 | return ret; |
231 | } | |
232 | ||
e16bcf98 TG |
233 | static int process_timeout(sd_rtnl *rtnl) { |
234 | _cleanup_sd_rtnl_message_unref_ sd_rtnl_message *m = NULL; | |
235 | struct reply_callback *c; | |
236 | usec_t n; | |
237 | int r; | |
238 | ||
239 | assert(rtnl); | |
240 | ||
241 | c = prioq_peek(rtnl->reply_callbacks_prioq); | |
242 | if (!c) | |
243 | return 0; | |
244 | ||
245 | n = now(CLOCK_MONOTONIC); | |
246 | if (c->timeout > n) | |
247 | return 0; | |
248 | ||
249 | r = message_new_synthetic_error(-ETIMEDOUT, c->serial, &m); | |
250 | if (r < 0) | |
251 | return r; | |
252 | ||
253 | assert_se(prioq_pop(rtnl->reply_callbacks_prioq) == c); | |
254 | hashmap_remove(rtnl->reply_callbacks, &c->serial); | |
255 | ||
256 | r = c->callback(rtnl, m, c->userdata); | |
257 | free(c); | |
258 | ||
259 | return r < 0 ? r : 1; | |
260 | } | |
261 | ||
262 | static int process_reply(sd_rtnl *rtnl, sd_rtnl_message *m) { | |
263 | struct reply_callback *c; | |
264 | uint64_t serial; | |
265 | int r; | |
266 | ||
267 | assert(rtnl); | |
268 | assert(m); | |
269 | ||
270 | serial = message_get_serial(m); | |
271 | c = hashmap_remove(rtnl->reply_callbacks, &serial); | |
272 | if (!c) | |
273 | return 0; | |
274 | ||
275 | if (c->timeout != 0) | |
276 | prioq_remove(rtnl->reply_callbacks_prioq, c, &c->prioq_idx); | |
277 | ||
278 | r = c->callback(rtnl, m, c->userdata); | |
279 | free(c); | |
280 | ||
281 | return r; | |
282 | } | |
283 | ||
4555ec72 TG |
284 | static int process_running(sd_rtnl *rtnl, sd_rtnl_message **ret) { |
285 | _cleanup_sd_rtnl_message_unref_ sd_rtnl_message *m = NULL; | |
286 | int r; | |
287 | ||
e16bcf98 TG |
288 | r = process_timeout(rtnl); |
289 | if (r != 0) | |
290 | goto null_message; | |
291 | ||
4555ec72 TG |
292 | r = dispatch_wqueue(rtnl); |
293 | if (r != 0) | |
294 | goto null_message; | |
295 | ||
296 | r = dispatch_rqueue(rtnl, &m); | |
297 | if (r < 0) | |
298 | return r; | |
299 | if (!m) | |
300 | goto null_message; | |
301 | ||
e16bcf98 TG |
302 | r = process_reply(rtnl, m); |
303 | if (r != 0) | |
304 | goto null_message; | |
305 | ||
4555ec72 TG |
306 | if (ret) { |
307 | *ret = m; | |
308 | m = NULL; | |
309 | ||
310 | return 1; | |
311 | } | |
312 | ||
313 | return 1; | |
314 | ||
315 | null_message: | |
316 | if (r >= 0 && ret) | |
317 | *ret = NULL; | |
318 | ||
319 | return r; | |
320 | } | |
e16bcf98 | 321 | |
4555ec72 | 322 | int sd_rtnl_process(sd_rtnl *rtnl, sd_rtnl_message **ret) { |
e16bcf98 | 323 | RTNL_DONT_DESTROY(rtnl); |
4555ec72 TG |
324 | int r; |
325 | ||
326 | assert_return(rtnl, -EINVAL); | |
327 | assert_return(!rtnl_pid_changed(rtnl), -ECHILD); | |
328 | assert_return(!rtnl->processing, -EBUSY); | |
329 | ||
330 | rtnl->processing = true; | |
331 | r = process_running(rtnl, ret); | |
332 | rtnl->processing = false; | |
333 | ||
334 | return r; | |
335 | } | |
336 | ||
337 | static usec_t calc_elapse(uint64_t usec) { | |
338 | if (usec == (uint64_t) -1) | |
339 | return 0; | |
340 | ||
341 | if (usec == 0) | |
342 | usec = RTNL_DEFAULT_TIMEOUT; | |
343 | ||
344 | return now(CLOCK_MONOTONIC) + usec; | |
345 | } | |
346 | ||
b4f2a5b1 | 347 | static int rtnl_poll(sd_rtnl *rtnl, bool need_more, uint64_t timeout_usec) { |
4555ec72 TG |
348 | struct pollfd p[1] = {}; |
349 | struct timespec ts; | |
b4f2a5b1 TG |
350 | usec_t m = (usec_t) -1; |
351 | int r, e; | |
352 | ||
353 | assert(rtnl); | |
4555ec72 | 354 | |
b4f2a5b1 TG |
355 | e = sd_rtnl_get_events(rtnl); |
356 | if (e < 0) | |
357 | return e; | |
4555ec72 | 358 | |
b4f2a5b1 TG |
359 | if (need_more) |
360 | /* Caller wants more data, and doesn't care about | |
361 | * what's been read or any other timeouts. */ | |
362 | return e |= POLLIN; | |
363 | else { | |
364 | usec_t until; | |
365 | /* Caller wants to process if there is something to | |
366 | * process, but doesn't care otherwise */ | |
367 | ||
368 | r = sd_rtnl_get_timeout(rtnl, &until); | |
369 | if (r < 0) | |
370 | return r; | |
371 | if (r > 0) { | |
372 | usec_t nw; | |
373 | nw = now(CLOCK_MONOTONIC); | |
374 | m = until > nw ? until - nw : 0; | |
375 | } | |
376 | } | |
65f568bb | 377 | |
b4f2a5b1 TG |
378 | if (timeout_usec != (uint64_t) -1 && (m == (uint64_t) -1 || timeout_usec < m)) |
379 | m = timeout_usec; | |
380 | ||
381 | p[0].fd = rtnl->fd; | |
382 | p[0].events = e; | |
383 | ||
384 | r = ppoll(p, 1, m == (uint64_t) -1 ? NULL : timespec_store(&ts, m), NULL); | |
4555ec72 TG |
385 | if (r < 0) |
386 | return -errno; | |
387 | ||
388 | return r > 0 ? 1 : 0; | |
389 | } | |
390 | ||
391 | int sd_rtnl_wait(sd_rtnl *nl, uint64_t timeout_usec) { | |
392 | assert_return(nl, -EINVAL); | |
393 | assert_return(!rtnl_pid_changed(nl), -ECHILD); | |
394 | ||
395 | if (nl->rqueue_size > 0) | |
396 | return 0; | |
397 | ||
b4f2a5b1 | 398 | return rtnl_poll(nl, false, timeout_usec); |
4555ec72 TG |
399 | } |
400 | ||
e16bcf98 TG |
401 | static int timeout_compare(const void *a, const void *b) { |
402 | const struct reply_callback *x = a, *y = b; | |
403 | ||
404 | if (x->timeout != 0 && y->timeout == 0) | |
405 | return -1; | |
406 | ||
407 | if (x->timeout == 0 && y->timeout != 0) | |
408 | return 1; | |
409 | ||
410 | if (x->timeout < y->timeout) | |
411 | return -1; | |
412 | ||
413 | if (x->timeout > y->timeout) | |
414 | return 1; | |
415 | ||
416 | return 0; | |
417 | } | |
418 | ||
419 | int sd_rtnl_call_async(sd_rtnl *nl, | |
420 | sd_rtnl_message *m, | |
421 | sd_rtnl_message_handler_t callback, | |
422 | void *userdata, | |
423 | uint64_t usec, | |
424 | uint32_t *serial) { | |
425 | struct reply_callback *c; | |
426 | uint32_t s; | |
427 | int r, k; | |
428 | ||
429 | assert_return(nl, -EINVAL); | |
430 | assert_return(m, -EINVAL); | |
431 | assert_return(callback, -EINVAL); | |
432 | assert_return(!rtnl_pid_changed(nl), -ECHILD); | |
433 | ||
434 | r = hashmap_ensure_allocated(&nl->reply_callbacks, uint64_hash_func, uint64_compare_func); | |
435 | if (r < 0) | |
436 | return r; | |
437 | ||
438 | if (usec != (uint64_t) -1) { | |
439 | r = prioq_ensure_allocated(&nl->reply_callbacks_prioq, timeout_compare); | |
440 | if (r < 0) | |
441 | return r; | |
442 | } | |
443 | ||
444 | c = new0(struct reply_callback, 1); | |
445 | if (!c) | |
446 | return -ENOMEM; | |
447 | ||
448 | c->callback = callback; | |
449 | c->userdata = userdata; | |
450 | c->timeout = calc_elapse(usec); | |
451 | ||
452 | k = sd_rtnl_send(nl, m, &s); | |
453 | if (k < 0) { | |
454 | free(c); | |
455 | return k; | |
456 | } | |
457 | ||
458 | c->serial = s; | |
459 | ||
460 | r = hashmap_put(nl->reply_callbacks, &c->serial, c); | |
461 | if (r < 0) { | |
462 | free(c); | |
463 | return r; | |
464 | } | |
465 | ||
466 | if (c->timeout != 0) { | |
467 | r = prioq_put(nl->reply_callbacks_prioq, c, &c->prioq_idx); | |
468 | if (r > 0) { | |
469 | c->timeout = 0; | |
470 | sd_rtnl_call_async_cancel(nl, c->serial); | |
471 | return r; | |
472 | } | |
473 | } | |
474 | ||
475 | if (serial) | |
476 | *serial = s; | |
477 | ||
478 | return k; | |
479 | } | |
480 | ||
481 | int sd_rtnl_call_async_cancel(sd_rtnl *nl, uint32_t serial) { | |
482 | struct reply_callback *c; | |
483 | uint64_t s = serial; | |
484 | ||
485 | assert_return(nl, -EINVAL); | |
486 | assert_return(serial != 0, -EINVAL); | |
487 | assert_return(!rtnl_pid_changed(nl), -ECHILD); | |
488 | ||
489 | c = hashmap_remove(nl->reply_callbacks, &s); | |
490 | if (!c) | |
491 | return 0; | |
492 | ||
493 | if (c->timeout != 0) | |
494 | prioq_remove(nl->reply_callbacks_prioq, c, &c->prioq_idx); | |
495 | ||
496 | free(c); | |
497 | return 1; | |
498 | } | |
499 | ||
4555ec72 TG |
500 | int sd_rtnl_call(sd_rtnl *nl, |
501 | sd_rtnl_message *message, | |
502 | uint64_t usec, | |
503 | sd_rtnl_message **ret) { | |
504 | usec_t timeout; | |
505 | uint32_t serial; | |
506 | bool room = false; | |
507 | int r; | |
508 | ||
509 | assert_return(nl, -EINVAL); | |
510 | assert_return(!rtnl_pid_changed(nl), -ECHILD); | |
511 | assert_return(message, -EINVAL); | |
512 | ||
513 | r = sd_rtnl_send(nl, message, &serial); | |
514 | if (r < 0) | |
515 | return r; | |
516 | ||
517 | timeout = calc_elapse(usec); | |
518 | ||
65f568bb | 519 | for (;;) { |
4555ec72 TG |
520 | usec_t left; |
521 | _cleanup_sd_rtnl_message_unref_ sd_rtnl_message *incoming = NULL; | |
fe5c4e49 | 522 | |
4555ec72 TG |
523 | if (!room) { |
524 | sd_rtnl_message **q; | |
65f568bb | 525 | |
4555ec72 TG |
526 | if (nl->rqueue_size >= RTNL_RQUEUE_MAX) |
527 | return -ENOBUFS; | |
65f568bb | 528 | |
4555ec72 TG |
529 | /* Make sure there's room for queueing this |
530 | * locally, before we read the message */ | |
65f568bb | 531 | |
4555ec72 TG |
532 | q = realloc(nl->rqueue, (nl->rqueue_size + 1) * sizeof(sd_rtnl_message*)); |
533 | if (!q) | |
534 | return -ENOMEM; | |
65f568bb | 535 | |
4555ec72 TG |
536 | nl->rqueue = q; |
537 | room = true; | |
538 | } | |
539 | ||
540 | r = socket_read_message(nl, &incoming); | |
65f568bb TG |
541 | if (r < 0) |
542 | return r; | |
4555ec72 TG |
543 | if (incoming) { |
544 | uint32_t received_serial = message_get_serial(incoming); | |
65f568bb TG |
545 | |
546 | if (received_serial == serial) { | |
e16bcf98 | 547 | r = sd_rtnl_message_get_errno(incoming); |
fe5c4e49 | 548 | if (r < 0) |
65f568bb | 549 | return r; |
65f568bb | 550 | |
fe5c4e49 | 551 | if (ret) { |
4555ec72 TG |
552 | *ret = incoming; |
553 | incoming = NULL; | |
fe5c4e49 | 554 | } |
65f568bb | 555 | |
4555ec72 | 556 | return 1; |
65f568bb | 557 | } |
4555ec72 TG |
558 | |
559 | /* Room was allocated on the queue above */ | |
560 | nl->rqueue[nl->rqueue_size ++] = incoming; | |
561 | incoming = NULL; | |
562 | room = false; | |
563 | ||
564 | /* Try to read more, right away */ | |
565 | continue; | |
65f568bb | 566 | } |
4555ec72 TG |
567 | if (r != 0) |
568 | continue; | |
65f568bb | 569 | |
4555ec72 TG |
570 | if (timeout > 0) { |
571 | usec_t n; | |
572 | ||
573 | n = now(CLOCK_MONOTONIC); | |
574 | if (n >= timeout) | |
575 | return -ETIMEDOUT; | |
576 | ||
577 | left = timeout - n; | |
578 | } else | |
579 | left = (uint64_t) -1; | |
580 | ||
b4f2a5b1 | 581 | r = rtnl_poll(nl, true, left); |
4555ec72 TG |
582 | if (r < 0) |
583 | return r; | |
584 | ||
585 | r = dispatch_wqueue(nl); | |
586 | if (r < 0) | |
587 | return r; | |
588 | } | |
65f568bb | 589 | } |
b4f2a5b1 TG |
590 | |
591 | int sd_rtnl_flush(sd_rtnl *rtnl) { | |
592 | int r; | |
593 | ||
594 | assert_return(rtnl, -EINVAL); | |
595 | assert_return(!rtnl_pid_changed(rtnl), -ECHILD); | |
596 | ||
597 | if (rtnl->wqueue_size <= 0) | |
598 | return 0; | |
599 | ||
600 | for (;;) { | |
601 | r = dispatch_wqueue(rtnl); | |
602 | if (r < 0) | |
603 | return r; | |
604 | ||
605 | if (rtnl->wqueue_size <= 0) | |
606 | return 0; | |
607 | ||
608 | r = rtnl_poll(rtnl, false, (uint64_t) -1); | |
609 | if (r < 0) | |
610 | return r; | |
611 | } | |
612 | } | |
613 | ||
614 | int sd_rtnl_get_events(sd_rtnl *rtnl) { | |
615 | int flags = 0; | |
616 | ||
617 | assert_return(rtnl, -EINVAL); | |
618 | assert_return(!rtnl_pid_changed(rtnl), -ECHILD); | |
619 | ||
620 | if (rtnl->rqueue_size <= 0) | |
621 | flags |= POLLIN; | |
622 | if (rtnl->wqueue_size > 0) | |
623 | flags |= POLLOUT; | |
624 | ||
625 | return flags; | |
626 | } | |
627 | ||
628 | int sd_rtnl_get_timeout(sd_rtnl *rtnl, uint64_t *timeout_usec) { | |
629 | struct reply_callback *c; | |
630 | ||
631 | assert_return(rtnl, -EINVAL); | |
632 | assert_return(timeout_usec, -EINVAL); | |
633 | assert_return(!rtnl_pid_changed(rtnl), -ECHILD); | |
634 | ||
635 | if (rtnl->rqueue_size > 0) { | |
636 | *timeout_usec = 0; | |
637 | return 1; | |
638 | } | |
639 | ||
640 | c = prioq_peek(rtnl->reply_callbacks_prioq); | |
641 | if (!c) { | |
642 | *timeout_usec = (uint64_t) -1; | |
643 | return 0; | |
644 | } | |
645 | ||
646 | *timeout_usec = c->timeout; | |
647 | ||
648 | return 1; | |
649 | } | |
650 | ||
651 | static int io_callback(sd_event_source *s, int fd, uint32_t revents, void *userdata) { | |
652 | sd_rtnl *rtnl = userdata; | |
653 | int r; | |
654 | ||
655 | assert(rtnl); | |
656 | ||
657 | r = sd_rtnl_process(rtnl, NULL); | |
658 | if (r < 0) | |
659 | return r; | |
660 | ||
661 | return 1; | |
662 | } | |
663 | ||
664 | static int time_callback(sd_event_source *s, uint64_t usec, void *userdata) { | |
665 | sd_rtnl *rtnl = userdata; | |
666 | int r; | |
667 | ||
668 | assert(rtnl); | |
669 | ||
670 | r = sd_rtnl_process(rtnl, NULL); | |
671 | if (r < 0) | |
672 | return r; | |
673 | ||
674 | return 1; | |
675 | } | |
676 | ||
677 | static int prepare_callback(sd_event_source *s, void *userdata) { | |
678 | sd_rtnl *rtnl = userdata; | |
679 | int r, e; | |
680 | usec_t until; | |
681 | ||
682 | assert(s); | |
683 | assert(rtnl); | |
684 | ||
685 | e = sd_rtnl_get_events(rtnl); | |
686 | if (e < 0) | |
687 | return e; | |
688 | ||
689 | r = sd_event_source_set_io_events(rtnl->io_event_source, e); | |
690 | if (r < 0) | |
691 | return r; | |
692 | ||
693 | r = sd_rtnl_get_timeout(rtnl, &until); | |
694 | if (r < 0) | |
695 | return r; | |
696 | if (r > 0) { | |
697 | int j; | |
698 | ||
699 | j = sd_event_source_set_time(rtnl->time_event_source, until); | |
700 | if (j < 0) | |
701 | return j; | |
702 | } | |
703 | ||
704 | r = sd_event_source_set_enabled(rtnl->time_event_source, r > 0); | |
705 | if (r < 0) | |
706 | return r; | |
707 | ||
708 | return 1; | |
709 | } | |
710 | ||
711 | static int quit_callback(sd_event_source *event, void *userdata) { | |
712 | sd_rtnl *rtnl = userdata; | |
713 | ||
714 | assert(event); | |
715 | ||
716 | sd_rtnl_flush(rtnl); | |
717 | ||
718 | return 1; | |
719 | } | |
720 | ||
721 | int sd_rtnl_attach_event(sd_rtnl *rtnl, sd_event *event, int priority) { | |
722 | int r; | |
723 | ||
724 | assert_return(rtnl, -EINVAL); | |
725 | assert_return(!rtnl->event, -EBUSY); | |
726 | ||
727 | assert(!rtnl->io_event_source); | |
728 | assert(!rtnl->time_event_source); | |
729 | ||
730 | if (event) | |
731 | rtnl->event = sd_event_ref(event); | |
732 | else { | |
733 | r = sd_event_default(&rtnl->event); | |
734 | if (r < 0) | |
735 | return r; | |
736 | } | |
737 | ||
738 | r = sd_event_add_io(rtnl->event, rtnl->fd, 0, io_callback, rtnl, &rtnl->io_event_source); | |
739 | if (r < 0) | |
740 | goto fail; | |
741 | ||
742 | r = sd_event_source_set_priority(rtnl->io_event_source, priority); | |
743 | if (r < 0) | |
744 | goto fail; | |
745 | ||
746 | r = sd_event_source_set_prepare(rtnl->io_event_source, prepare_callback); | |
747 | if (r < 0) | |
748 | goto fail; | |
749 | ||
750 | r = sd_event_add_monotonic(rtnl->event, 0, 0, time_callback, rtnl, &rtnl->time_event_source); | |
751 | if (r < 0) | |
752 | goto fail; | |
753 | ||
754 | r = sd_event_source_set_priority(rtnl->time_event_source, priority); | |
755 | if (r < 0) | |
756 | goto fail; | |
757 | ||
758 | r = sd_event_add_quit(rtnl->event, quit_callback, rtnl, &rtnl->quit_event_source); | |
759 | if (r < 0) | |
760 | goto fail; | |
761 | ||
762 | return 0; | |
763 | ||
764 | fail: | |
765 | sd_rtnl_detach_event(rtnl); | |
766 | return r; | |
767 | } | |
768 | ||
769 | int sd_rtnl_detach_event(sd_rtnl *rtnl) { | |
770 | assert_return(rtnl, -EINVAL); | |
771 | assert_return(rtnl->event, -ENXIO); | |
772 | ||
773 | if (rtnl->io_event_source) | |
774 | rtnl->io_event_source = sd_event_source_unref(rtnl->io_event_source); | |
775 | ||
776 | if (rtnl->time_event_source) | |
777 | rtnl->time_event_source = sd_event_source_unref(rtnl->time_event_source); | |
778 | ||
779 | if (rtnl->quit_event_source) | |
780 | rtnl->quit_event_source = sd_event_source_unref(rtnl->quit_event_source); | |
781 | ||
782 | if (rtnl->event) | |
783 | rtnl->event = sd_event_unref(rtnl->event); | |
784 | ||
785 | return 0; | |
786 | } |