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