]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/libsystemd-rtnl/sd-rtnl.c
rtnl: don't ignore broadcast messages
[thirdparty/systemd.git] / src / libsystemd-rtnl / sd-rtnl.c
CommitLineData
65f568bb
TG
1/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
2
3/***
4 This file is part of systemd.
5
6 Copyright 2013 Tom Gundersen <teg@jklm.no>
7
8 systemd is free software; you can redistribute it and/or modify it
9 under the terms of the GNU Lesser General Public License as published by
10 the Free Software Foundation; either version 2.1 of the License, or
11 (at your option) any later version.
12
13 systemd is distributed in the hope that it will be useful, but
14 WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 Lesser General Public License for more details.
17
18 You should have received a copy of the GNU Lesser General Public License
19 along with systemd; If not, see <http://www.gnu.org/licenses/>.
20***/
21
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
33static 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
62static 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 71int 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
102sd_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
109sd_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
134int 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
179static 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
208static 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
233static 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
262static 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
284static 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
315null_message:
316 if (r >= 0 && ret)
317 *ret = NULL;
318
319 return r;
320}
e16bcf98 321
4555ec72 322int 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
337static 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 347static 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
391int 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
401static 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
419int 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
481int 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
500int 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
591int 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
614int 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
628int 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
651static 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
664static 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
677static 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
711static 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
721int 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
764fail:
765 sd_rtnl_detach_event(rtnl);
766 return r;
767}
768
769int 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}