chiark / gitweb /
bus: fix callback index when dispatching kernel messages
[elogind.git] / src / libsystemd-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         /* 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
58         *ret = rtnl;
59         return 0;
60 }
61
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
71 int sd_rtnl_open(uint32_t groups, sd_rtnl **ret) {
72         _cleanup_sd_rtnl_unref_ sd_rtnl *rtnl = NULL;
73         socklen_t addrlen;
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);
81         if (rtnl->fd < 0)
82                 return -errno;
83
84         rtnl->sockaddr.nl.nl_groups = groups;
85
86         addrlen = sizeof(rtnl->sockaddr);
87
88         r = bind(rtnl->fd, &rtnl->sockaddr.sa, addrlen);
89         if (r < 0)
90                 return -errno;
91
92         r = getsockname(rtnl->fd, &rtnl->sockaddr.sa, &addrlen);
93         if (r < 0)
94                 return r;
95
96         *ret = rtnl;
97         rtnl = NULL;
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) {
110
111         if (rtnl && REFCNT_DEC(rtnl->n_ref) <= 0) {
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
122                 hashmap_free_free(rtnl->reply_callbacks);
123                 prioq_free(rtnl->reply_callbacks_prioq);
124
125                 if (rtnl->fd >= 0)
126                         close_nointr_nofail(rtnl->fd);
127
128                 free(rtnl);
129         }
130
131         return NULL;
132 }
133
134 int sd_rtnl_send(sd_rtnl *nl,
135                  sd_rtnl_message *message,
136                  uint32_t *serial) {
137         int r;
138
139         assert_return(nl, -EINVAL);
140         assert_return(!rtnl_pid_changed(nl), -ECHILD);
141         assert_return(message, -EINVAL);
142
143         r = message_seal(nl, message);
144         if (r < 0)
145                 return r;
146
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;
160
161                 /* append to queue */
162                 if (nl->wqueue_size >= RTNL_WQUEUE_MAX)
163                         return -ENOBUFS;
164
165                 q = realloc(nl->wqueue, sizeof(sd_rtnl_message*) * (nl->wqueue_size + 1));
166                 if (!q)
167                         return -ENOMEM;
168
169                 nl->wqueue = q;
170                 q[nl->wqueue_size ++] = sd_rtnl_message_ref(message);
171         }
172
173         if (serial)
174                 *serial = message_get_serial(message);
175
176         return 1;
177 }
178
179 static int dispatch_rqueue(sd_rtnl *rtnl, sd_rtnl_message **message) {
180         sd_rtnl_message *z = NULL;
181         int r;
182
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]);
215                 if (r < 0)
216                         return r;
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;
227                 }
228         }
229
230         return ret;
231 }
232
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
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
288         r = process_timeout(rtnl);
289         if (r != 0)
290                 goto null_message;
291
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
302         r = process_reply(rtnl, m);
303         if (r != 0)
304                 goto null_message;
305
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 }
321
322 int sd_rtnl_process(sd_rtnl *rtnl, sd_rtnl_message **ret) {
323         RTNL_DONT_DESTROY(rtnl);
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
347 static int rtnl_poll(sd_rtnl *rtnl, bool need_more, uint64_t timeout_usec) {
348         struct pollfd p[1] = {};
349         struct timespec ts;
350         usec_t m = (usec_t) -1;
351         int r, e;
352
353         assert(rtnl);
354
355         e = sd_rtnl_get_events(rtnl);
356         if (e < 0)
357                 return e;
358
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         }
377
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);
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
398         return rtnl_poll(nl, false, timeout_usec);
399 }
400
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
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
519         for (;;) {
520                 usec_t left;
521                 _cleanup_sd_rtnl_message_unref_ sd_rtnl_message *incoming = NULL;
522
523                 if (!room) {
524                         sd_rtnl_message **q;
525
526                         if (nl->rqueue_size >= RTNL_RQUEUE_MAX)
527                                 return -ENOBUFS;
528
529                         /* Make sure there's room for queueing this
530                          * locally, before we read the message */
531
532                         q = realloc(nl->rqueue, (nl->rqueue_size + 1) * sizeof(sd_rtnl_message*));
533                         if (!q)
534                                 return -ENOMEM;
535
536                         nl->rqueue = q;
537                         room = true;
538                 }
539
540                 r = socket_read_message(nl, &incoming);
541                 if (r < 0)
542                         return r;
543                 if (incoming) {
544                         uint32_t received_serial = message_get_serial(incoming);
545
546                         if (received_serial == serial) {
547                                 r = sd_rtnl_message_get_errno(incoming);
548                                 if (r < 0)
549                                         return r;
550
551                                 if (ret) {
552                                         *ret = incoming;
553                                         incoming = NULL;
554                                 }
555
556                                 return 1;
557                         }
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;
566                 }
567                 if (r != 0)
568                         continue;
569
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
581                 r = rtnl_poll(nl, true, left);
582                 if (r < 0)
583                         return r;
584
585                 r = dispatch_wqueue(nl);
586                 if (r < 0)
587                         return r;
588         }
589 }
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 }