1 /* SPDX-License-Identifier: LGPL-2.1-or-later */
5 #include "alloc-util.h"
10 #include "string-util.h"
11 #include "time-util.h"
14 static void curl_glue_check_finished(CurlGlue
*g
) {
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
);
22 return; /* exit scheduled? Then don't process this anymore */
24 log_debug_errno(r
, "Unexpected error while checking for event loop exit code, ignoring: %m");
28 msg
= curl_multi_info_read(g
->curl
, &k
);
32 if (msg
->msg
== CURLMSG_DONE
&& g
->on_finished
)
33 g
->on_finished(g
, msg
->easy_handle
, msg
->data
.result
);
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
);
39 static int curl_glue_on_io(sd_event_source
*s
, int fd
, uint32_t revents
, void *userdata
) {
40 CurlGlue
*g
= ASSERT_PTR(userdata
);
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
;
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.");
58 curl_glue_check_finished(g
);
62 static 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
);
70 if (action
== CURL_POLL_REMOVE
) {
72 sd_event_source_disable_unref(io
);
74 hashmap_remove(g
->ios
, FD_TO_PTR(s
));
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
)
84 r
= hashmap_ensure_allocated(&g
->ios
, &trivial_hash_ops
);
90 if (action
== CURL_POLL_IN
)
92 else if (action
== CURL_POLL_OUT
)
94 else if (action
== CURL_POLL_INOUT
)
95 events
= EPOLLIN
|EPOLLOUT
;
98 if (sd_event_source_set_io_events(io
, events
) < 0)
101 if (sd_event_source_set_enabled(io
, SD_EVENT_ON
) < 0)
104 if (sd_event_add_io(g
->event
, &io
, s
, events
, curl_glue_on_io
, g
) < 0)
107 if (curl_multi_assign(g
->curl
, s
, io
) != CURLM_OK
)
110 (void) sd_event_source_set_description(io
, "curl-io");
112 r
= hashmap_put(g
->ios
, FD_TO_PTR(s
), io
);
115 sd_event_source_unref(io
);
123 static int curl_glue_on_timer(sd_event_source
*s
, uint64_t usec
, void *userdata
) {
124 CurlGlue
*g
= ASSERT_PTR(userdata
);
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.");
133 curl_glue_check_finished(g
);
137 static int curl_glue_timer_callback(CURLM
*curl
, long timeout_ms
, void *userdata
) {
138 CurlGlue
*g
= ASSERT_PTR(userdata
);
143 /* Don't configure timer anymore when the event loop is dead already. */
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
)
150 if (timeout_ms
< 0) {
151 if (sd_event_source_set_enabled(g
->timer
, SD_EVENT_OFF
) < 0)
157 usec
= (usec_t
) timeout_ms
* USEC_PER_MSEC
+ USEC_PER_MSEC
- 1;
160 if (sd_event_source_set_time_relative(g
->timer
, usec
) < 0)
163 if (sd_event_source_set_enabled(g
->timer
, SD_EVENT_ONESHOT
) < 0)
166 if (sd_event_add_time_relative(g
->event
, &g
->timer
, CLOCK_BOOTTIME
, usec
, 0, curl_glue_on_timer
, g
) < 0)
169 (void) sd_event_source_set_description(g
->timer
, "curl-timer");
175 static int curl_glue_on_defer(sd_event_source
*s
, void *userdata
) {
176 CurlGlue
*g
= ASSERT_PTR(userdata
);
180 curl_glue_check_finished(g
);
184 CurlGlue
*curl_glue_unref(CurlGlue
*g
) {
191 curl_multi_cleanup(g
->curl
);
193 while ((io
= hashmap_steal_first(g
->ios
)))
194 sd_event_source_unref(io
);
196 hashmap_free(g
->ios
);
198 sd_event_source_disable_unref(g
->timer
);
199 sd_event_source_disable_unref(g
->defer
);
200 sd_event_unref(g
->event
);
204 int 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
;
211 e
= sd_event_ref(event
);
213 r
= sd_event_default(&e
);
218 c
= curl_multi_init();
222 g
= new(CurlGlue
, 1);
227 .event
= TAKE_PTR(e
),
231 if (curl_multi_setopt(g
->curl
, CURLMOPT_SOCKETDATA
, g
) != CURLM_OK
)
234 if (curl_multi_setopt(g
->curl
, CURLMOPT_SOCKETFUNCTION
, curl_glue_socket_callback
) != CURLM_OK
)
237 if (curl_multi_setopt(g
->curl
, CURLMOPT_TIMERDATA
, g
) != CURLM_OK
)
240 if (curl_multi_setopt(g
->curl
, CURLMOPT_TIMERFUNCTION
, curl_glue_timer_callback
) != CURLM_OK
)
243 r
= sd_event_add_defer(g
->event
, &g
->defer
, curl_glue_on_defer
, g
);
247 (void) sd_event_source_set_description(g
->defer
, "curl-defer");
254 int curl_glue_make(CURL
**ret
, const char *url
, void *userdata
) {
255 _cleanup_(curl_easy_cleanupp
) CURL
*c
= NULL
;
256 const char *useragent
;
261 c
= curl_easy_init();
266 (void) curl_easy_setopt(c
, CURLOPT_VERBOSE
, 1L);
268 if (curl_easy_setopt(c
, CURLOPT_URL
, url
) != CURLE_OK
)
271 if (curl_easy_setopt(c
, CURLOPT_PRIVATE
, userdata
) != CURLE_OK
)
274 useragent
= strjoina(program_invocation_short_name
, "/" GIT_VERSION
);
275 if (curl_easy_setopt(c
, CURLOPT_USERAGENT
, useragent
) != CURLE_OK
)
278 if (curl_easy_setopt(c
, CURLOPT_FOLLOWLOCATION
, 1L) != CURLE_OK
)
281 if (curl_easy_setopt(c
, CURLOPT_NOSIGNAL
, 1L) != CURLE_OK
)
284 if (curl_easy_setopt(c
, CURLOPT_LOW_SPEED_TIME
, 60L) != CURLE_OK
)
287 if (curl_easy_setopt(c
, CURLOPT_LOW_SPEED_LIMIT
, 30L) != CURLE_OK
)
290 #if LIBCURL_VERSION_NUM >= 0x075500 /* libcurl 7.85.0 */
291 if (curl_easy_setopt(c
, CURLOPT_PROTOCOLS_STR
, "HTTP,HTTPS,FILE") != CURLE_OK
)
293 if (curl_easy_setopt(c
, CURLOPT_PROTOCOLS
, CURLPROTO_HTTP
|CURLPROTO_HTTPS
|CURLPROTO_FILE
) != CURLE_OK
)
301 int curl_glue_add(CurlGlue
*g
, CURL
*c
) {
305 if (curl_multi_add_handle(g
->curl
, c
) != CURLM_OK
)
311 void curl_glue_remove_and_free(CurlGlue
*g
, CURL
*c
) {
318 curl_multi_remove_handle(g
->curl
, c
);
320 curl_easy_cleanup(c
);
323 struct curl_slist
*curl_slist_new(const char *first
, ...) {
324 struct curl_slist
*l
;
330 l
= curl_slist_append(NULL
, first
);
337 struct curl_slist
*n
;
340 i
= va_arg(ap
, const char*);
344 n
= curl_slist_append(l
, i
);
347 curl_slist_free_all(l
);
358 int curl_header_strdup(const void *contents
, size_t sz
, const char *field
, char **value
) {
362 p
= memory_startswith_no_case(contents
, sz
, field
);
366 sz
-= p
- (const char*) contents
;
368 if (memchr(p
, 0, sz
))
371 /* Skip over preceding whitespace */
372 while (sz
> 0 && strchr(WHITESPACE
, p
[0])) {
377 /* Truncate trailing whitespace */
378 while (sz
> 0 && strchr(WHITESPACE
, p
[sz
-1]))
389 int curl_parse_http_time(const char *t
, usec_t
*ret
) {
393 time_t v
= curl_getdate(t
, NULL
);
394 if (v
== (time_t) -1)
397 if ((usec_t
) v
>= USEC_INFINITY
/ USEC_PER_SEC
) /* check overflow */
400 *ret
= (usec_t
) v
* USEC_PER_SEC
;