]> git.ipfire.org Git - thirdparty/systemd.git/blame_incremental - src/import/curl-util.c
Add systemd-analyze verb to list runtime unit properties (#37665)
[thirdparty/systemd.git] / src / import / curl-util.c
... / ...
CommitLineData
1/* SPDX-License-Identifier: LGPL-2.1-or-later */
2
3#include "sd-event.h"
4
5#include "alloc-util.h"
6#include "curl-util.h"
7#include "fd-util.h"
8#include "hashmap.h"
9#include "log.h"
10#include "string-util.h"
11#include "time-util.h"
12#include "version.h"
13
14static void curl_glue_check_finished(CurlGlue *g) {
15 int r;
16
17 assert(g);
18
19 /* sd_event_get_exit_code() returns -ENODATA if no exit was scheduled yet */
20 r = sd_event_get_exit_code(g->event, /* ret= */ NULL);
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;
28 msg = curl_multi_info_read(g->curl, &k);
29 if (!msg)
30 return;
31
32 if (msg->msg == CURLMSG_DONE && g->on_finished)
33 g->on_finished(g, msg->easy_handle, msg->data.result);
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);
37}
38
39static int curl_glue_on_io(sd_event_source *s, int fd, uint32_t revents, void *userdata) {
40 CurlGlue *g = ASSERT_PTR(userdata);
41 int action, k = 0;
42
43 assert(s);
44
45 if (FLAGS_SET(revents, EPOLLIN | EPOLLOUT))
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
54 if (curl_multi_socket_action(g->curl, fd, action, &k) != CURLM_OK)
55 return log_debug_errno(SYNTHETIC_ERRNO(EINVAL),
56 "Failed to propagate IO event.");
57
58 curl_glue_check_finished(g);
59 return 0;
60}
61
62static int curl_glue_socket_callback(CURL *curl, curl_socket_t s, int action, void *userdata, void *socketp) {
63 sd_event_source *io = socketp;
64 CurlGlue *g = ASSERT_PTR(userdata);
65 uint32_t events = 0;
66 int r;
67
68 assert(curl);
69
70 if (action == CURL_POLL_REMOVE) {
71 if (io) {
72 sd_event_source_disable_unref(io);
73
74 hashmap_remove(g->ios, FD_TO_PTR(s));
75 }
76
77 return 0;
78 }
79
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
84 r = hashmap_ensure_allocated(&g->ios, &trivial_hash_ops);
85 if (r < 0) {
86 log_oom();
87 return -1;
88 }
89
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 {
104 if (sd_event_add_io(g->event, &io, s, events, curl_glue_on_io, g) < 0)
105 return -1;
106
107 if (curl_multi_assign(g->curl, s, io) != CURLM_OK)
108 return -1;
109
110 (void) sd_event_source_set_description(io, "curl-io");
111
112 r = hashmap_put(g->ios, FD_TO_PTR(s), io);
113 if (r < 0) {
114 log_oom();
115 sd_event_source_unref(io);
116 return -1;
117 }
118 }
119
120 return 0;
121}
122
123static int curl_glue_on_timer(sd_event_source *s, uint64_t usec, void *userdata) {
124 CurlGlue *g = ASSERT_PTR(userdata);
125 int k = 0;
126
127 assert(s);
128
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.");
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) {
138 CurlGlue *g = ASSERT_PTR(userdata);
139 usec_t usec;
140
141 assert(curl);
142
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
150 if (timeout_ms < 0) {
151 if (sd_event_source_set_enabled(g->timer, SD_EVENT_OFF) < 0)
152 return -1;
153
154 return 0;
155 }
156
157 usec = (usec_t) timeout_ms * USEC_PER_MSEC + USEC_PER_MSEC - 1;
158
159 if (g->timer) {
160 if (sd_event_source_set_time_relative(g->timer, usec) < 0)
161 return -1;
162
163 if (sd_event_source_set_enabled(g->timer, SD_EVENT_ONESHOT) < 0)
164 return -1;
165 } else {
166 if (sd_event_add_time_relative(g->event, &g->timer, CLOCK_BOOTTIME, usec, 0, curl_glue_on_timer, g) < 0)
167 return -1;
168
169 (void) sd_event_source_set_description(g->timer, "curl-timer");
170 }
171
172 return 0;
173}
174
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
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
193 while ((io = hashmap_steal_first(g->ios)))
194 sd_event_source_unref(io);
195
196 hashmap_free(g->ios);
197
198 sd_event_source_disable_unref(g->timer);
199 sd_event_source_disable_unref(g->defer);
200 sd_event_unref(g->event);
201 return mfree(g);
202}
203
204int curl_glue_new(CurlGlue **glue, sd_event *event) {
205 _cleanup_(curl_glue_unrefp) CurlGlue *g = NULL;
206 _cleanup_(curl_multi_cleanupp) CURLM *c = NULL;
207 _cleanup_(sd_event_unrefp) sd_event *e = NULL;
208 int r;
209
210 if (event)
211 e = sd_event_ref(event);
212 else {
213 r = sd_event_default(&e);
214 if (r < 0)
215 return r;
216 }
217
218 c = curl_multi_init();
219 if (!c)
220 return -ENOMEM;
221
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
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
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
249 *glue = TAKE_PTR(g);
250
251 return 0;
252}
253
254int curl_glue_make(CURL **ret, const char *url, void *userdata) {
255 _cleanup_(curl_easy_cleanupp) CURL *c = NULL;
256 const char *useragent;
257
258 assert(ret);
259 assert(url);
260
261 c = curl_easy_init();
262 if (!c)
263 return -ENOMEM;
264
265 if (DEBUG_LOGGING)
266 (void) curl_easy_setopt(c, CURLOPT_VERBOSE, 1L);
267
268 if (curl_easy_setopt(c, CURLOPT_URL, url) != CURLE_OK)
269 return -EIO;
270
271 if (curl_easy_setopt(c, CURLOPT_PRIVATE, userdata) != CURLE_OK)
272 return -EIO;
273
274 useragent = strjoina(program_invocation_short_name, "/" GIT_VERSION);
275 if (curl_easy_setopt(c, CURLOPT_USERAGENT, useragent) != CURLE_OK)
276 return -EIO;
277
278 if (curl_easy_setopt(c, CURLOPT_FOLLOWLOCATION, 1L) != CURLE_OK)
279 return -EIO;
280
281 if (curl_easy_setopt(c, CURLOPT_NOSIGNAL, 1L) != CURLE_OK)
282 return -EIO;
283
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
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
293 if (curl_easy_setopt(c, CURLOPT_PROTOCOLS, CURLPROTO_HTTP|CURLPROTO_HTTPS|CURLPROTO_FILE) != CURLE_OK)
294#endif
295 return -EIO;
296
297 *ret = TAKE_PTR(c);
298 return 0;
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) {
359 const char *p;
360 char *s;
361
362 p = memory_startswith_no_case(contents, sz, field);
363 if (!p)
364 return 0;
365
366 sz -= p - (const char*) contents;
367
368 if (memchr(p, 0, sz))
369 return 0;
370
371 /* Skip over preceding whitespace */
372 while (sz > 0 && strchr(WHITESPACE, p[0])) {
373 p++;
374 sz--;
375 }
376
377 /* Truncate trailing whitespace */
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}
388
389int curl_parse_http_time(const char *t, usec_t *ret) {
390 assert(t);
391 assert(ret);
392
393 time_t v = curl_getdate(t, NULL);
394 if (v == (time_t) -1)
395 return -EINVAL;
396
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;
403}