]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/import/curl-util.c
openssl-util: allow to build with openssl without UI support (#38041)
[thirdparty/systemd.git] / src / import / curl-util.c
CommitLineData
db9ecf05 1/* SPDX-License-Identifier: LGPL-2.1-or-later */
72648326 2
9412e9e9 3#include "sd-event.h"
ca78ad1d 4
b5efdb8a 5#include "alloc-util.h"
72648326 6#include "curl-util.h"
3ffd4af2 7#include "fd-util.h"
9412e9e9 8#include "hashmap.h"
93a1f792 9#include "log.h"
3ffd4af2 10#include "string-util.h"
9412e9e9 11#include "time-util.h"
47350c5f 12#include "version.h"
72648326
LP
13
14static void curl_glue_check_finished(CurlGlue *g) {
8bd4d506 15 int r;
72648326
LP
16
17 assert(g);
18
8bd4d506 19 /* sd_event_get_exit_code() returns -ENODATA if no exit was scheduled yet */
1cce9d93 20 r = sd_event_get_exit_code(g->event, /* ret= */ NULL);
8bd4d506
LP
21 if (r >= 0)
22 return; /* exit scheduled? Then don't process this anymore */
23 if (r != -ENODATA)
24 log_debug_errno(r, "Unexpected error while checking for event loop exit code, ignoring: %m");
25
26 CURLMsg *msg;
27 int k = 0;
72648326
LP
28 msg = curl_multi_info_read(g->curl, &k);
29 if (!msg)
30 return;
31
8bd4d506 32 if (msg->msg == CURLMSG_DONE && g->on_finished)
72648326 33 g->on_finished(g, msg->easy_handle, msg->data.result);
8bd4d506
LP
34
35 /* This is a queue, process another item soon, but do so in a later event loop iteration. */
36 (void) sd_event_source_set_enabled(g->defer, SD_EVENT_ONESHOT);
72648326
LP
37}
38
39static int curl_glue_on_io(sd_event_source *s, int fd, uint32_t revents, void *userdata) {
99534007 40 CurlGlue *g = ASSERT_PTR(userdata);
d8c72e87 41 int action, k = 0;
72648326
LP
42
43 assert(s);
72648326 44
d94a24ca 45 if (FLAGS_SET(revents, EPOLLIN | EPOLLOUT))
72648326
LP
46 action = CURL_POLL_INOUT;
47 else if (revents & EPOLLIN)
48 action = CURL_POLL_IN;
49 else if (revents & EPOLLOUT)
50 action = CURL_POLL_OUT;
51 else
52 action = 0;
53
d8c72e87 54 if (curl_multi_socket_action(g->curl, fd, action, &k) != CURLM_OK)
baaa35ad
ZJS
55 return log_debug_errno(SYNTHETIC_ERRNO(EINVAL),
56 "Failed to propagate IO event.");
72648326
LP
57
58 curl_glue_check_finished(g);
59 return 0;
60}
61
5b639090 62static int curl_glue_socket_callback(CURL *curl, curl_socket_t s, int action, void *userdata, void *socketp) {
d8c72e87 63 sd_event_source *io = socketp;
99534007 64 CurlGlue *g = ASSERT_PTR(userdata);
72648326
LP
65 uint32_t events = 0;
66 int r;
67
68 assert(curl);
72648326 69
72648326
LP
70 if (action == CURL_POLL_REMOVE) {
71 if (io) {
1d3fe304 72 sd_event_source_disable_unref(io);
72648326 73
23e096cc 74 hashmap_remove(g->ios, FD_TO_PTR(s));
72648326
LP
75 }
76
77 return 0;
78 }
79
5b2926d9
YW
80 /* Don't configure io event source anymore when the event loop is dead already. */
81 if (g->event && sd_event_get_state(g->event) == SD_EVENT_FINISHED)
82 return 0;
83
72648326
LP
84 r = hashmap_ensure_allocated(&g->ios, &trivial_hash_ops);
85 if (r < 0) {
86 log_oom();
87 return -1;
88 }
89
72648326
LP
90 if (action == CURL_POLL_IN)
91 events = EPOLLIN;
92 else if (action == CURL_POLL_OUT)
93 events = EPOLLOUT;
94 else if (action == CURL_POLL_INOUT)
95 events = EPOLLIN|EPOLLOUT;
96
97 if (io) {
98 if (sd_event_source_set_io_events(io, events) < 0)
99 return -1;
100
101 if (sd_event_source_set_enabled(io, SD_EVENT_ON) < 0)
102 return -1;
103 } else {
d8c72e87 104 if (sd_event_add_io(g->event, &io, s, events, curl_glue_on_io, g) < 0)
72648326
LP
105 return -1;
106
d8c72e87 107 if (curl_multi_assign(g->curl, s, io) != CURLM_OK)
72648326
LP
108 return -1;
109
edfd706d 110 (void) sd_event_source_set_description(io, "curl-io");
72648326 111
23e096cc 112 r = hashmap_put(g->ios, FD_TO_PTR(s), io);
72648326
LP
113 if (r < 0) {
114 log_oom();
115 sd_event_source_unref(io);
116 return -1;
117 }
72648326
LP
118 }
119
120 return 0;
121}
122
123static int curl_glue_on_timer(sd_event_source *s, uint64_t usec, void *userdata) {
99534007 124 CurlGlue *g = ASSERT_PTR(userdata);
72648326
LP
125 int k = 0;
126
127 assert(s);
72648326 128
baaa35ad
ZJS
129 if (curl_multi_socket_action(g->curl, CURL_SOCKET_TIMEOUT, 0, &k) != CURLM_OK)
130 return log_debug_errno(SYNTHETIC_ERRNO(EINVAL),
131 "Failed to propagate timeout.");
72648326
LP
132
133 curl_glue_check_finished(g);
134 return 0;
135}
136
137static int curl_glue_timer_callback(CURLM *curl, long timeout_ms, void *userdata) {
99534007 138 CurlGlue *g = ASSERT_PTR(userdata);
72648326
LP
139 usec_t usec;
140
141 assert(curl);
72648326 142
c5ecf094
LP
143 /* Don't configure timer anymore when the event loop is dead already. */
144 if (g->timer) {
145 sd_event *event_loop = sd_event_source_get_event(g->timer);
146 if (event_loop && sd_event_get_state(event_loop) == SD_EVENT_FINISHED)
147 return 0;
148 }
149
72648326 150 if (timeout_ms < 0) {
a3ada90a
ZJS
151 if (sd_event_source_set_enabled(g->timer, SD_EVENT_OFF) < 0)
152 return -1;
72648326
LP
153
154 return 0;
155 }
156
39cf0351 157 usec = (usec_t) timeout_ms * USEC_PER_MSEC + USEC_PER_MSEC - 1;
72648326
LP
158
159 if (g->timer) {
39cf0351 160 if (sd_event_source_set_time_relative(g->timer, usec) < 0)
72648326
LP
161 return -1;
162
163 if (sd_event_source_set_enabled(g->timer, SD_EVENT_ONESHOT) < 0)
164 return -1;
165 } else {
ba4e0427 166 if (sd_event_add_time_relative(g->event, &g->timer, CLOCK_BOOTTIME, usec, 0, curl_glue_on_timer, g) < 0)
72648326
LP
167 return -1;
168
edfd706d 169 (void) sd_event_source_set_description(g->timer, "curl-timer");
72648326
LP
170 }
171
172 return 0;
173}
174
8bd4d506
LP
175static int curl_glue_on_defer(sd_event_source *s, void *userdata) {
176 CurlGlue *g = ASSERT_PTR(userdata);
177
178 assert(s);
179
180 curl_glue_check_finished(g);
181 return 0;
182}
183
72648326
LP
184CurlGlue *curl_glue_unref(CurlGlue *g) {
185 sd_event_source *io;
186
187 if (!g)
188 return NULL;
189
190 if (g->curl)
191 curl_multi_cleanup(g->curl);
192
38cd55b0 193 while ((io = hashmap_steal_first(g->ios)))
72648326 194 sd_event_source_unref(io);
72648326
LP
195
196 hashmap_free(g->ios);
197
8bd4d506
LP
198 sd_event_source_disable_unref(g->timer);
199 sd_event_source_disable_unref(g->defer);
72648326 200 sd_event_unref(g->event);
6b430fdb 201 return mfree(g);
72648326
LP
202}
203
204int curl_glue_new(CurlGlue **glue, sd_event *event) {
205 _cleanup_(curl_glue_unrefp) CurlGlue *g = NULL;
2d052a0a 206 _cleanup_(curl_multi_cleanupp) CURLM *c = NULL;
0d94088e 207 _cleanup_(sd_event_unrefp) sd_event *e = NULL;
72648326
LP
208 int r;
209
72648326 210 if (event)
0d94088e 211 e = sd_event_ref(event);
72648326 212 else {
0d94088e 213 r = sd_event_default(&e);
72648326
LP
214 if (r < 0)
215 return r;
216 }
217
0d94088e
YW
218 c = curl_multi_init();
219 if (!c)
72648326
LP
220 return -ENOMEM;
221
0d94088e
YW
222 g = new(CurlGlue, 1);
223 if (!g)
224 return -ENOMEM;
225
226 *g = (CurlGlue) {
227 .event = TAKE_PTR(e),
228 .curl = TAKE_PTR(c),
229 };
230
72648326
LP
231 if (curl_multi_setopt(g->curl, CURLMOPT_SOCKETDATA, g) != CURLM_OK)
232 return -EINVAL;
233
234 if (curl_multi_setopt(g->curl, CURLMOPT_SOCKETFUNCTION, curl_glue_socket_callback) != CURLM_OK)
235 return -EINVAL;
236
237 if (curl_multi_setopt(g->curl, CURLMOPT_TIMERDATA, g) != CURLM_OK)
238 return -EINVAL;
239
240 if (curl_multi_setopt(g->curl, CURLMOPT_TIMERFUNCTION, curl_glue_timer_callback) != CURLM_OK)
241 return -EINVAL;
242
8bd4d506
LP
243 r = sd_event_add_defer(g->event, &g->defer, curl_glue_on_defer, g);
244 if (r < 0)
245 return r;
246
247 (void) sd_event_source_set_description(g->defer, "curl-defer");
248
1cc6c93a 249 *glue = TAKE_PTR(g);
72648326
LP
250
251 return 0;
252}
253
254int curl_glue_make(CURL **ret, const char *url, void *userdata) {
c3e65800 255 _cleanup_(curl_easy_cleanupp) CURL *c = NULL;
72648326 256 const char *useragent;
72648326
LP
257
258 assert(ret);
259 assert(url);
260
261 c = curl_easy_init();
262 if (!c)
263 return -ENOMEM;
264
8dc0291c
LP
265 if (DEBUG_LOGGING)
266 (void) curl_easy_setopt(c, CURLOPT_VERBOSE, 1L);
72648326 267
c3e65800
YW
268 if (curl_easy_setopt(c, CURLOPT_URL, url) != CURLE_OK)
269 return -EIO;
72648326 270
c3e65800
YW
271 if (curl_easy_setopt(c, CURLOPT_PRIVATE, userdata) != CURLE_OK)
272 return -EIO;
72648326 273
681bd2c5 274 useragent = strjoina(program_invocation_short_name, "/" GIT_VERSION);
c3e65800
YW
275 if (curl_easy_setopt(c, CURLOPT_USERAGENT, useragent) != CURLE_OK)
276 return -EIO;
72648326 277
c3e65800
YW
278 if (curl_easy_setopt(c, CURLOPT_FOLLOWLOCATION, 1L) != CURLE_OK)
279 return -EIO;
f85df818
LP
280
281 if (curl_easy_setopt(c, CURLOPT_NOSIGNAL, 1L) != CURLE_OK)
282 return -EIO;
72648326 283
d076f9fd
LP
284 if (curl_easy_setopt(c, CURLOPT_LOW_SPEED_TIME, 60L) != CURLE_OK)
285 return -EIO;
286
287 if (curl_easy_setopt(c, CURLOPT_LOW_SPEED_LIMIT, 30L) != CURLE_OK)
288 return -EIO;
289
e61a4c0b
FS
290#if LIBCURL_VERSION_NUM >= 0x075500 /* libcurl 7.85.0 */
291 if (curl_easy_setopt(c, CURLOPT_PROTOCOLS_STR, "HTTP,HTTPS,FILE") != CURLE_OK)
292#else
55b90ee0 293 if (curl_easy_setopt(c, CURLOPT_PROTOCOLS, CURLPROTO_HTTP|CURLPROTO_HTTPS|CURLPROTO_FILE) != CURLE_OK)
e61a4c0b 294#endif
55b90ee0
LP
295 return -EIO;
296
67577508 297 *ret = TAKE_PTR(c);
72648326 298 return 0;
72648326
LP
299}
300
301int curl_glue_add(CurlGlue *g, CURL *c) {
302 assert(g);
303 assert(c);
304
305 if (curl_multi_add_handle(g->curl, c) != CURLM_OK)
306 return -EIO;
307
308 return 0;
309}
310
311void curl_glue_remove_and_free(CurlGlue *g, CURL *c) {
312 assert(g);
313
314 if (!c)
315 return;
316
317 if (g->curl)
318 curl_multi_remove_handle(g->curl, c);
319
320 curl_easy_cleanup(c);
321}
322
323struct curl_slist *curl_slist_new(const char *first, ...) {
324 struct curl_slist *l;
325 va_list ap;
326
327 if (!first)
328 return NULL;
329
330 l = curl_slist_append(NULL, first);
331 if (!l)
332 return NULL;
333
334 va_start(ap, first);
335
336 for (;;) {
337 struct curl_slist *n;
338 const char *i;
339
340 i = va_arg(ap, const char*);
341 if (!i)
342 break;
343
344 n = curl_slist_append(l, i);
345 if (!n) {
346 va_end(ap);
347 curl_slist_free_all(l);
348 return NULL;
349 }
350
351 l = n;
352 }
353
354 va_end(ap);
355 return l;
356}
357
358int curl_header_strdup(const void *contents, size_t sz, const char *field, char **value) {
d27b725a 359 const char *p;
72648326
LP
360 char *s;
361
21224070 362 p = memory_startswith_no_case(contents, sz, field);
d27b725a 363 if (!p)
72648326
LP
364 return 0;
365
d27b725a 366 sz -= p - (const char*) contents;
72648326
LP
367
368 if (memchr(p, 0, sz))
369 return 0;
370
5238e957 371 /* Skip over preceding whitespace */
72648326
LP
372 while (sz > 0 && strchr(WHITESPACE, p[0])) {
373 p++;
374 sz--;
375 }
376
13e785f7 377 /* Truncate trailing whitespace */
72648326
LP
378 while (sz > 0 && strchr(WHITESPACE, p[sz-1]))
379 sz--;
380
381 s = strndup(p, sz);
382 if (!s)
383 return -ENOMEM;
384
385 *value = s;
386 return 1;
387}
90199220 388
5fa89b2c 389int curl_parse_http_time(const char *t, usec_t *ret) {
90199220
LP
390 assert(t);
391 assert(ret);
392
9e471990 393 time_t v = curl_getdate(t, NULL);
394 if (v == (time_t) -1)
0193ad26 395 return -EINVAL;
90199220 396
9e471990 397 if ((usec_t) v >= USEC_INFINITY / USEC_PER_SEC) /* check overflow */
398 return -ERANGE;
399
400 *ret = (usec_t) v * USEC_PER_SEC;
401
402 return 0;
90199220 403}