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