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