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