chiark / gitweb /
rtnl: start adding support for asynchronous messaging
[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
28 #include "sd-rtnl.h"
29 #include "rtnl-internal.h"
30 #include "rtnl-util.h"
31
32 static int sd_rtnl_new(sd_rtnl **ret) {
33         sd_rtnl *rtnl;
34
35         assert_return(ret, -EINVAL);
36
37         rtnl = new0(sd_rtnl, 1);
38         if (!rtnl)
39                 return -ENOMEM;
40
41         rtnl->n_ref = REFCNT_INIT;
42
43         rtnl->fd = -1;
44
45         rtnl->sockaddr.nl.nl_family = AF_NETLINK;
46
47         rtnl->original_pid = getpid();
48
49         /* We guarantee that wqueue always has space for at least
50          * one entry */
51         rtnl->wqueue = new(sd_rtnl_message*, 1);
52         if (!rtnl->wqueue) {
53                 free(rtnl);
54                 return -ENOMEM;
55         }
56
57         *ret = rtnl;
58         return 0;
59 }
60
61 static bool rtnl_pid_changed(sd_rtnl *rtnl) {
62         assert(rtnl);
63
64         /* We don't support people creating an rtnl connection and
65          * keeping it around over a fork(). Let's complain. */
66
67         return rtnl->original_pid != getpid();
68 }
69
70 int sd_rtnl_open(uint32_t groups, sd_rtnl **ret) {
71         _cleanup_sd_rtnl_unref_ sd_rtnl *rtnl = NULL;
72         socklen_t addrlen;
73         int r;
74
75         r = sd_rtnl_new(&rtnl);
76         if (r < 0)
77                 return r;
78
79         rtnl->fd = socket(PF_NETLINK, SOCK_RAW|SOCK_CLOEXEC|SOCK_NONBLOCK, NETLINK_ROUTE);
80         if (rtnl->fd < 0)
81                 return -errno;
82
83         rtnl->sockaddr.nl.nl_groups = groups;
84
85         addrlen = sizeof(rtnl->sockaddr);
86
87         r = bind(rtnl->fd, &rtnl->sockaddr.sa, addrlen);
88         if (r < 0)
89                 return -errno;
90
91         r = getsockname(rtnl->fd, &rtnl->sockaddr.sa, &addrlen);
92         if (r < 0)
93                 return r;
94
95         *ret = rtnl;
96         rtnl = NULL;
97
98         return 0;
99 }
100
101 sd_rtnl *sd_rtnl_ref(sd_rtnl *rtnl) {
102         if (rtnl)
103                 assert_se(REFCNT_INC(rtnl->n_ref) >= 2);
104
105         return rtnl;
106 }
107
108 sd_rtnl *sd_rtnl_unref(sd_rtnl *rtnl) {
109
110         if (rtnl && REFCNT_DEC(rtnl->n_ref) <= 0) {
111                 unsigned i;
112
113                 for (i = 0; i < rtnl->rqueue_size; i++)
114                         sd_rtnl_message_unref(rtnl->rqueue[i]);
115                 free(rtnl->rqueue);
116
117                 for (i = 0; i < rtnl->wqueue_size; i++)
118                         sd_rtnl_message_unref(rtnl->wqueue[i]);
119                 free(rtnl->wqueue);
120
121                 if (rtnl->fd >= 0)
122                         close_nointr_nofail(rtnl->fd);
123
124                 free(rtnl);
125         }
126
127         return NULL;
128 }
129
130 int sd_rtnl_send(sd_rtnl *nl,
131                  sd_rtnl_message *message,
132                  uint32_t *serial) {
133         int r;
134
135         assert_return(nl, -EINVAL);
136         assert_return(!rtnl_pid_changed(nl), -ECHILD);
137         assert_return(message, -EINVAL);
138
139         r = message_seal(nl, message);
140         if (r < 0)
141                 return r;
142
143         if (nl->wqueue_size <= 0) {
144                 /* send directly */
145                 r = socket_write_message(nl, message);
146                 if (r < 0)
147                         return r;
148                 else if (r == 0) {
149                         /* nothing was sent, so let's put it on
150                          * the queue */
151                         nl->wqueue[0] = sd_rtnl_message_ref(message);
152                         nl->wqueue_size = 1;
153                 }
154         } else {
155                 sd_rtnl_message **q;
156
157                 /* append to queue */
158                 if (nl->wqueue_size >= RTNL_WQUEUE_MAX)
159                         return -ENOBUFS;
160
161                 q = realloc(nl->wqueue, sizeof(sd_rtnl_message*) * (nl->wqueue_size + 1));
162                 if (!q)
163                         return -ENOMEM;
164
165                 nl->wqueue = q;
166                 q[nl->wqueue_size ++] = sd_rtnl_message_ref(message);
167         }
168
169         if (serial)
170                 *serial = message_get_serial(message);
171
172         return 1;
173 }
174
175 static int dispatch_rqueue(sd_rtnl *rtnl, sd_rtnl_message **message) {
176         sd_rtnl_message *z = NULL;
177         int r;
178
179         assert(rtnl);
180         assert(message);
181
182         if (rtnl->rqueue_size > 0) {
183                 /* Dispatch a queued message */
184
185                 *message = rtnl->rqueue[0];
186                 rtnl->rqueue_size --;
187                 memmove(rtnl->rqueue, rtnl->rqueue + 1, sizeof(sd_rtnl_message*) * rtnl->rqueue_size);
188
189                 return 1;
190         }
191
192         /* Try to read a new message */
193         r = socket_read_message(rtnl, &z);
194         if (r < 0)
195                 return r;
196         if (r == 0)
197                 return 0;
198
199         *message = z;
200
201         return 1;
202 }
203
204 static int dispatch_wqueue(sd_rtnl *rtnl) {
205         int r, ret = 0;
206
207         assert(rtnl);
208
209         while (rtnl->wqueue_size > 0) {
210                 r = socket_write_message(rtnl, rtnl->wqueue[0]);
211                 if (r < 0)
212                         return r;
213                 else if (r == 0)
214                         /* Didn't do anything this time */
215                         return ret;
216                 else {
217                         /* see equivalent in sd-bus.c */
218                         sd_rtnl_message_unref(rtnl->wqueue[0]);
219                         rtnl->wqueue_size --;
220                         memmove(rtnl->wqueue, rtnl->wqueue + 1, sizeof(sd_rtnl_message*) * rtnl->wqueue_size);
221
222                         ret = 1;
223                 }
224         }
225
226         return ret;
227 }
228
229 static int process_running(sd_rtnl *rtnl, sd_rtnl_message **ret) {
230         _cleanup_sd_rtnl_message_unref_ sd_rtnl_message *m = NULL;
231         int r;
232
233         r = dispatch_wqueue(rtnl);
234         if (r != 0)
235                 goto null_message;
236
237         r = dispatch_rqueue(rtnl, &m);
238         if (r < 0)
239                 return r;
240         if (!m)
241                 goto null_message;
242
243         if (ret) {
244                 *ret = m;
245                 m = NULL;
246
247                 return 1;
248         }
249
250         return 1;
251
252 null_message:
253         if (r >= 0 && ret)
254                 *ret = NULL;
255
256         return r;
257 }
258 int sd_rtnl_process(sd_rtnl *rtnl, sd_rtnl_message **ret) {
259         int r;
260
261         assert_return(rtnl, -EINVAL);
262         assert_return(!rtnl_pid_changed(rtnl), -ECHILD);
263         assert_return(!rtnl->processing, -EBUSY);
264
265         rtnl->processing = true;
266         r = process_running(rtnl, ret);
267         rtnl->processing = false;
268
269         return r;
270 }
271
272 static usec_t calc_elapse(uint64_t usec) {
273         if (usec == (uint64_t) -1)
274                 return 0;
275
276         if (usec == 0)
277                 usec = RTNL_DEFAULT_TIMEOUT;
278
279         return now(CLOCK_MONOTONIC) + usec;
280 }
281
282 static int rtnl_poll(sd_rtnl *nl, uint64_t timeout_usec) {
283         struct pollfd p[1] = {};
284         struct timespec ts;
285         int r;
286
287         assert(nl);
288
289         p[0].fd = nl->fd;
290         p[0].events = POLLIN;
291
292         r = ppoll(p, 1, timeout_usec == (uint64_t) -1 ? NULL :
293                         timespec_store(&ts, timeout_usec), NULL);
294         if (r < 0)
295                 return -errno;
296
297         return r > 0 ? 1 : 0;
298 }
299
300 int sd_rtnl_wait(sd_rtnl *nl, uint64_t timeout_usec) {
301         assert_return(nl, -EINVAL);
302         assert_return(!rtnl_pid_changed(nl), -ECHILD);
303
304         if (nl->rqueue_size > 0)
305                 return 0;
306
307         return rtnl_poll(nl, timeout_usec);
308 }
309
310 int sd_rtnl_call(sd_rtnl *nl,
311                 sd_rtnl_message *message,
312                 uint64_t usec,
313                 sd_rtnl_message **ret) {
314         usec_t timeout;
315         uint32_t serial;
316         bool room = false;
317         int r;
318
319         assert_return(nl, -EINVAL);
320         assert_return(!rtnl_pid_changed(nl), -ECHILD);
321         assert_return(message, -EINVAL);
322
323         r = sd_rtnl_send(nl, message, &serial);
324         if (r < 0)
325                 return r;
326
327         timeout = calc_elapse(usec);
328
329         for (;;) {
330                 usec_t left;
331                 _cleanup_sd_rtnl_message_unref_ sd_rtnl_message *incoming = NULL;
332
333                 if (!room) {
334                         sd_rtnl_message **q;
335
336                         if (nl->rqueue_size >= RTNL_RQUEUE_MAX)
337                                 return -ENOBUFS;
338
339                         /* Make sure there's room for queueing this
340                          * locally, before we read the message */
341
342                         q = realloc(nl->rqueue, (nl->rqueue_size + 1) * sizeof(sd_rtnl_message*));
343                         if (!q)
344                                 return -ENOMEM;
345
346                         nl->rqueue = q;
347                         room = true;
348                 }
349
350                 r = socket_read_message(nl, &incoming);
351                 if (r < 0)
352                         return r;
353                 if (incoming) {
354                         uint32_t received_serial = message_get_serial(incoming);
355
356                         if (received_serial == serial) {
357                                 r = message_get_errno(incoming);
358                                 if (r < 0)
359                                         return r;
360
361                                 if (ret) {
362                                         *ret = incoming;
363                                         incoming = NULL;
364                                 }
365
366                                 return 1;
367                         }
368
369                         /* Room was allocated on the queue above */
370                         nl->rqueue[nl->rqueue_size ++] = incoming;
371                         incoming = NULL;
372                         room = false;
373
374                         /* Try to read more, right away */
375                         continue;
376                 }
377                 if (r != 0)
378                         continue;
379
380                 if (timeout > 0) {
381                         usec_t n;
382
383                         n = now(CLOCK_MONOTONIC);
384                         if (n >= timeout)
385                                 return -ETIMEDOUT;
386
387                         left = timeout - n;
388                 } else
389                         left = (uint64_t) -1;
390
391                 r = rtnl_poll(nl, left);
392                 if (r < 0)
393                         return r;
394
395                 r = dispatch_wqueue(nl);
396                 if (r < 0)
397                         return r;
398         }
399 }