chiark / gitweb /
import: add new minimal tool "systemd-import" for pulling down foreign containers...
[elogind.git] / src / import / curl-util.c
1 /*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
2
3 /***
4   This file is part of systemd.
5
6   Copyright 2014 Lennart Poettering
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 "curl-util.h"
23
24 static void curl_glue_check_finished(CurlGlue *g) {
25         CURLMsg *msg;
26         int k = 0;
27
28         assert(g);
29
30         msg = curl_multi_info_read(g->curl, &k);
31         if (!msg)
32                 return;
33
34         if (msg->msg != CURLMSG_DONE)
35                 return;
36
37         if (g->on_finished)
38                 g->on_finished(g, msg->easy_handle, msg->data.result);
39 }
40
41 static int curl_glue_on_io(sd_event_source *s, int fd, uint32_t revents, void *userdata) {
42         CurlGlue *g = userdata;
43         int action, k = 0, translated_fd;
44
45         assert(s);
46         assert(g);
47
48         translated_fd = PTR_TO_INT(hashmap_get(g->translate_fds, INT_TO_PTR(fd+1)));
49         assert(translated_fd > 0);
50         translated_fd--;
51
52         if ((revents & (EPOLLIN|EPOLLOUT)) == (EPOLLIN|EPOLLOUT))
53                 action = CURL_POLL_INOUT;
54         else if (revents & EPOLLIN)
55                 action = CURL_POLL_IN;
56         else if (revents & EPOLLOUT)
57                 action = CURL_POLL_OUT;
58         else
59                 action = 0;
60
61         if (curl_multi_socket_action(g->curl, translated_fd, action, &k) < 0) {
62                 log_debug("Failed to propagate IO event.");
63                 return -EINVAL;
64         }
65
66         curl_glue_check_finished(g);
67         return 0;
68 }
69
70 static int curl_glue_socket_callback(CURLM *curl, curl_socket_t s, int action, void *userdata, void *socketp) {
71         sd_event_source *io;
72         CurlGlue *g = userdata;
73         uint32_t events = 0;
74         int r;
75
76         assert(curl);
77         assert(g);
78
79         io = hashmap_get(g->ios, INT_TO_PTR(s+1));
80
81         if (action == CURL_POLL_REMOVE) {
82                 if (io) {
83                         int fd;
84
85                         fd = sd_event_source_get_io_fd(io);
86                         assert(fd >= 0);
87
88                         sd_event_source_set_enabled(io, SD_EVENT_OFF);
89                         sd_event_source_unref(io);
90
91                         hashmap_remove(g->ios, INT_TO_PTR(s+1));
92                         hashmap_remove(g->translate_fds, INT_TO_PTR(fd+1));
93
94                         safe_close(fd);
95                 }
96
97                 return 0;
98         }
99
100         r = hashmap_ensure_allocated(&g->ios, &trivial_hash_ops);
101         if (r < 0) {
102                 log_oom();
103                 return -1;
104         }
105
106         r = hashmap_ensure_allocated(&g->translate_fds, &trivial_hash_ops);
107         if (r < 0) {
108                 log_oom();
109                 return -1;
110         }
111
112         if (action == CURL_POLL_IN)
113                 events = EPOLLIN;
114         else if (action == CURL_POLL_OUT)
115                 events = EPOLLOUT;
116         else if (action == CURL_POLL_INOUT)
117                 events = EPOLLIN|EPOLLOUT;
118
119         if (io) {
120                 if (sd_event_source_set_io_events(io, events) < 0)
121                         return -1;
122
123                 if (sd_event_source_set_enabled(io, SD_EVENT_ON) < 0)
124                         return -1;
125         } else {
126                 _cleanup_close_ int fd = -1;
127
128                 /* When curl needs to remove an fd from us it closes
129                  * the fd first, and only then calls into us. This is
130                  * nasty, since we cannot pass the fd on to epoll()
131                  * anymore. Hence, duplicate the fds here, and keep a
132                  * copy for epoll which we control after use. */
133
134                 fd = fcntl(s, F_DUPFD_CLOEXEC, 3);
135                 if (fd < 0)
136                         return -1;
137
138                 if (sd_event_add_io(g->event, &io, fd, events, curl_glue_on_io, g) < 0)
139                         return -1;
140
141                 sd_event_source_set_description(io, "curl-io");
142
143                 r = hashmap_put(g->ios, INT_TO_PTR(s+1), io);
144                 if (r < 0) {
145                         log_oom();
146                         sd_event_source_unref(io);
147                         return -1;
148                 }
149
150                 r = hashmap_put(g->translate_fds, INT_TO_PTR(fd+1), INT_TO_PTR(s+1));
151                 if (r < 0) {
152                         log_oom();
153                         hashmap_remove(g->ios, INT_TO_PTR(s+1));
154                         sd_event_source_unref(io);
155                         return -1;
156                 }
157
158                 fd = -1;
159         }
160
161         return 0;
162 }
163
164 static int curl_glue_on_timer(sd_event_source *s, uint64_t usec, void *userdata) {
165         CurlGlue *g = userdata;
166         int k = 0;
167
168         assert(s);
169         assert(g);
170
171         if (curl_multi_socket_action(g->curl, CURL_SOCKET_TIMEOUT, 0, &k) != CURLM_OK) {
172                 log_debug("Failed to propagate timeout.");
173                 return -EINVAL;
174         }
175
176         curl_glue_check_finished(g);
177         return 0;
178 }
179
180 static int curl_glue_timer_callback(CURLM *curl, long timeout_ms, void *userdata) {
181         CurlGlue *g = userdata;
182         usec_t usec;
183
184         assert(curl);
185         assert(g);
186
187         if (timeout_ms < 0) {
188                 if (g->timer) {
189                         if (sd_event_source_set_enabled(g->timer, SD_EVENT_OFF) < 0)
190                                 return -1;
191                 }
192
193                 return 0;
194         }
195
196         usec = now(clock_boottime_or_monotonic()) + (usec_t) timeout_ms * USEC_PER_MSEC + USEC_PER_MSEC - 1;
197
198         if (g->timer) {
199                 if (sd_event_source_set_time(g->timer, usec) < 0)
200                         return -1;
201
202                 if (sd_event_source_set_enabled(g->timer, SD_EVENT_ONESHOT) < 0)
203                         return -1;
204         } else {
205                 if (sd_event_add_time(g->event, &g->timer, clock_boottime_or_monotonic(), usec, 0, curl_glue_on_timer, g) < 0)
206                         return -1;
207
208                 sd_event_source_set_description(g->timer, "curl-timer");
209         }
210
211         return 0;
212 }
213
214 CurlGlue *curl_glue_unref(CurlGlue *g) {
215         sd_event_source *io;
216
217         if (!g)
218                 return NULL;
219
220         if (g->curl)
221                 curl_multi_cleanup(g->curl);
222
223         while ((io = hashmap_steal_first(g->ios))) {
224                 int fd;
225
226                 fd = sd_event_source_get_io_fd(io);
227                 assert(fd >= 0);
228
229                 hashmap_remove(g->translate_fds, INT_TO_PTR(fd+1));
230
231                 safe_close(fd);
232                 sd_event_source_unref(io);
233         }
234
235         hashmap_free(g->ios);
236
237         sd_event_source_unref(g->timer);
238         sd_event_unref(g->event);
239
240         return NULL;
241 }
242
243 int curl_glue_new(CurlGlue **glue, sd_event *event) {
244         _cleanup_(curl_glue_unrefp) CurlGlue *g = NULL;
245         int r;
246
247         g = new0(CurlGlue, 1);
248         if (!g)
249                 return -ENOMEM;
250
251         if (event)
252                 g->event = sd_event_ref(event);
253         else {
254                 r = sd_event_default(&g->event);
255                 if (r < 0)
256                         return r;
257         }
258
259         g->curl = curl_multi_init();
260         if (!g->curl)
261                 return -ENOMEM;
262
263         if (curl_multi_setopt(g->curl, CURLMOPT_SOCKETDATA, g) != CURLM_OK)
264                 return -EINVAL;
265
266         if (curl_multi_setopt(g->curl, CURLMOPT_SOCKETFUNCTION, curl_glue_socket_callback) != CURLM_OK)
267                 return -EINVAL;
268
269         if (curl_multi_setopt(g->curl, CURLMOPT_TIMERDATA, g) != CURLM_OK)
270                 return -EINVAL;
271
272         if (curl_multi_setopt(g->curl, CURLMOPT_TIMERFUNCTION, curl_glue_timer_callback) != CURLM_OK)
273                 return -EINVAL;
274
275         *glue = g;
276         g = NULL;
277
278         return 0;
279 }
280
281 int curl_glue_make(CURL **ret, const char *url, void *userdata) {
282         const char *useragent;
283         CURL *c;
284         int r;
285
286         assert(ret);
287         assert(url);
288
289         c = curl_easy_init();
290         if (!c)
291                 return -ENOMEM;
292
293         /* curl_easy_setopt(c, CURLOPT_VERBOSE, 1L); */
294
295         if (curl_easy_setopt(c, CURLOPT_URL, url) != CURLE_OK) {
296                 r = -EIO;
297                 goto fail;
298         }
299
300         if (curl_easy_setopt(c, CURLOPT_PRIVATE, userdata) != CURLE_OK) {
301                 r = -EIO;
302                 goto fail;
303         }
304
305         useragent = strappenda(program_invocation_short_name, "/" PACKAGE_VERSION);
306         if (curl_easy_setopt(c, CURLOPT_USERAGENT, useragent) != CURLE_OK) {
307                 r = -EIO;
308                 goto fail;
309         }
310
311         if (curl_easy_setopt(c, CURLOPT_FOLLOWLOCATION, 1L) != CURLE_OK) {
312                 r = -EIO;
313                 goto fail;
314         }
315
316         *ret = c;
317         return 0;
318
319 fail:
320         curl_easy_cleanup(c);
321         return r;
322 }
323
324 int curl_glue_add(CurlGlue *g, CURL *c) {
325         assert(g);
326         assert(c);
327
328         if (curl_multi_add_handle(g->curl, c) != CURLM_OK)
329                 return -EIO;
330
331         return 0;
332 }
333
334 void curl_glue_remove_and_free(CurlGlue *g, CURL *c) {
335         assert(g);
336
337         if (!c)
338                 return;
339
340         if (g->curl)
341                 curl_multi_remove_handle(g->curl, c);
342
343         curl_easy_cleanup(c);
344 }
345
346 struct curl_slist *curl_slist_new(const char *first, ...) {
347         struct curl_slist *l;
348         va_list ap;
349
350         if (!first)
351                 return NULL;
352
353         l = curl_slist_append(NULL, first);
354         if (!l)
355                 return NULL;
356
357         va_start(ap, first);
358
359         for (;;) {
360                 struct curl_slist *n;
361                 const char *i;
362
363                 i = va_arg(ap, const char*);
364                 if (!i)
365                         break;
366
367                 n = curl_slist_append(l, i);
368                 if (!n) {
369                         va_end(ap);
370                         curl_slist_free_all(l);
371                         return NULL;
372                 }
373
374                 l = n;
375         }
376
377         va_end(ap);
378         return l;
379 }
380
381 int curl_header_strdup(const void *contents, size_t sz, const char *field, char **value) {
382         const char *p = contents;
383         size_t l;
384         char *s;
385
386         l = strlen(field);
387         if (sz < l)
388                 return 0;
389
390         if (memcmp(p, field, l) != 0)
391                 return 0;
392
393         p += l;
394         sz -= l;
395
396         if (memchr(p, 0, sz))
397                 return 0;
398
399         /* Skip over preceeding whitespace */
400         while (sz > 0 && strchr(WHITESPACE, p[0])) {
401                 p++;
402                 sz--;
403         }
404
405         /* Truncate trailing whitespace*/
406         while (sz > 0 && strchr(WHITESPACE, p[sz-1]))
407                 sz--;
408
409         s = strndup(p, sz);
410         if (!s)
411                 return -ENOMEM;
412
413         *value = s;
414         return 1;
415 }