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