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