1 /*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
4 This file is part of systemd.
6 Copyright 2014 Lennart Poettering
8 systemd is free software; you can redistribute it and/or modify it
9 under the terms of the GNU Lesser General Public License as published by
10 the Free Software Foundation; either version 2.1 of the License, or
11 (at your option) any later version.
13 systemd is distributed in the hope that it will be useful, but
14 WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 Lesser General Public License for more details.
18 You should have received a copy of the GNU Lesser General Public License
19 along with systemd; If not, see <http://www.gnu.org/licenses/>.
22 #include "alloc-util.h"
23 #include "curl-util.h"
25 #include "string-util.h"
27 static void curl_glue_check_finished(CurlGlue
*g
) {
33 msg
= curl_multi_info_read(g
->curl
, &k
);
37 if (msg
->msg
!= CURLMSG_DONE
)
41 g
->on_finished(g
, msg
->easy_handle
, msg
->data
.result
);
44 static int curl_glue_on_io(sd_event_source
*s
, int fd
, uint32_t revents
, void *userdata
) {
45 CurlGlue
*g
= userdata
;
46 int action
, k
= 0, translated_fd
;
51 translated_fd
= PTR_TO_INT(hashmap_get(g
->translate_fds
, INT_TO_PTR(fd
+1)));
52 assert(translated_fd
> 0);
55 if ((revents
& (EPOLLIN
|EPOLLOUT
)) == (EPOLLIN
|EPOLLOUT
))
56 action
= CURL_POLL_INOUT
;
57 else if (revents
& EPOLLIN
)
58 action
= CURL_POLL_IN
;
59 else if (revents
& EPOLLOUT
)
60 action
= CURL_POLL_OUT
;
64 if (curl_multi_socket_action(g
->curl
, translated_fd
, action
, &k
) < 0) {
65 log_debug("Failed to propagate IO event.");
69 curl_glue_check_finished(g
);
73 static int curl_glue_socket_callback(CURLM
*curl
, curl_socket_t s
, int action
, void *userdata
, void *socketp
) {
75 CurlGlue
*g
= userdata
;
82 io
= hashmap_get(g
->ios
, INT_TO_PTR(s
+1));
84 if (action
== CURL_POLL_REMOVE
) {
88 fd
= sd_event_source_get_io_fd(io
);
91 sd_event_source_set_enabled(io
, SD_EVENT_OFF
);
92 sd_event_source_unref(io
);
94 hashmap_remove(g
->ios
, INT_TO_PTR(s
+1));
95 hashmap_remove(g
->translate_fds
, INT_TO_PTR(fd
+1));
103 r
= hashmap_ensure_allocated(&g
->ios
, &trivial_hash_ops
);
109 r
= hashmap_ensure_allocated(&g
->translate_fds
, &trivial_hash_ops
);
115 if (action
== CURL_POLL_IN
)
117 else if (action
== CURL_POLL_OUT
)
119 else if (action
== CURL_POLL_INOUT
)
120 events
= EPOLLIN
|EPOLLOUT
;
123 if (sd_event_source_set_io_events(io
, events
) < 0)
126 if (sd_event_source_set_enabled(io
, SD_EVENT_ON
) < 0)
129 _cleanup_close_
int fd
= -1;
131 /* When curl needs to remove an fd from us it closes
132 * the fd first, and only then calls into us. This is
133 * nasty, since we cannot pass the fd on to epoll()
134 * anymore. Hence, duplicate the fds here, and keep a
135 * copy for epoll which we control after use. */
137 fd
= fcntl(s
, F_DUPFD_CLOEXEC
, 3);
141 if (sd_event_add_io(g
->event
, &io
, fd
, events
, curl_glue_on_io
, g
) < 0)
144 sd_event_source_set_description(io
, "curl-io");
146 r
= hashmap_put(g
->ios
, INT_TO_PTR(s
+1), io
);
149 sd_event_source_unref(io
);
153 r
= hashmap_put(g
->translate_fds
, INT_TO_PTR(fd
+1), INT_TO_PTR(s
+1));
156 hashmap_remove(g
->ios
, INT_TO_PTR(s
+1));
157 sd_event_source_unref(io
);
167 static int curl_glue_on_timer(sd_event_source
*s
, uint64_t usec
, void *userdata
) {
168 CurlGlue
*g
= userdata
;
174 if (curl_multi_socket_action(g
->curl
, CURL_SOCKET_TIMEOUT
, 0, &k
) != CURLM_OK
) {
175 log_debug("Failed to propagate timeout.");
179 curl_glue_check_finished(g
);
183 static int curl_glue_timer_callback(CURLM
*curl
, long timeout_ms
, void *userdata
) {
184 CurlGlue
*g
= userdata
;
190 if (timeout_ms
< 0) {
192 if (sd_event_source_set_enabled(g
->timer
, SD_EVENT_OFF
) < 0)
199 usec
= now(clock_boottime_or_monotonic()) + (usec_t
) timeout_ms
* USEC_PER_MSEC
+ USEC_PER_MSEC
- 1;
202 if (sd_event_source_set_time(g
->timer
, usec
) < 0)
205 if (sd_event_source_set_enabled(g
->timer
, SD_EVENT_ONESHOT
) < 0)
208 if (sd_event_add_time(g
->event
, &g
->timer
, clock_boottime_or_monotonic(), usec
, 0, curl_glue_on_timer
, g
) < 0)
211 sd_event_source_set_description(g
->timer
, "curl-timer");
217 CurlGlue
*curl_glue_unref(CurlGlue
*g
) {
224 curl_multi_cleanup(g
->curl
);
226 while ((io
= hashmap_steal_first(g
->ios
))) {
229 fd
= sd_event_source_get_io_fd(io
);
232 hashmap_remove(g
->translate_fds
, INT_TO_PTR(fd
+1));
235 sd_event_source_unref(io
);
238 hashmap_free(g
->ios
);
240 sd_event_source_unref(g
->timer
);
241 sd_event_unref(g
->event
);
247 int curl_glue_new(CurlGlue
**glue
, sd_event
*event
) {
248 _cleanup_(curl_glue_unrefp
) CurlGlue
*g
= NULL
;
251 g
= new0(CurlGlue
, 1);
256 g
->event
= sd_event_ref(event
);
258 r
= sd_event_default(&g
->event
);
263 g
->curl
= curl_multi_init();
267 if (curl_multi_setopt(g
->curl
, CURLMOPT_SOCKETDATA
, g
) != CURLM_OK
)
270 if (curl_multi_setopt(g
->curl
, CURLMOPT_SOCKETFUNCTION
, curl_glue_socket_callback
) != CURLM_OK
)
273 if (curl_multi_setopt(g
->curl
, CURLMOPT_TIMERDATA
, g
) != CURLM_OK
)
276 if (curl_multi_setopt(g
->curl
, CURLMOPT_TIMERFUNCTION
, curl_glue_timer_callback
) != CURLM_OK
)
285 int curl_glue_make(CURL
**ret
, const char *url
, void *userdata
) {
286 const char *useragent
;
293 c
= curl_easy_init();
297 /* curl_easy_setopt(c, CURLOPT_VERBOSE, 1L); */
299 if (curl_easy_setopt(c
, CURLOPT_URL
, url
) != CURLE_OK
) {
304 if (curl_easy_setopt(c
, CURLOPT_PRIVATE
, userdata
) != CURLE_OK
) {
309 useragent
= strjoina(program_invocation_short_name
, "/" PACKAGE_VERSION
);
310 if (curl_easy_setopt(c
, CURLOPT_USERAGENT
, useragent
) != CURLE_OK
) {
315 if (curl_easy_setopt(c
, CURLOPT_FOLLOWLOCATION
, 1L) != CURLE_OK
) {
324 curl_easy_cleanup(c
);
328 int curl_glue_add(CurlGlue
*g
, CURL
*c
) {
332 if (curl_multi_add_handle(g
->curl
, c
) != CURLM_OK
)
338 void curl_glue_remove_and_free(CurlGlue
*g
, CURL
*c
) {
345 curl_multi_remove_handle(g
->curl
, c
);
347 curl_easy_cleanup(c
);
350 struct curl_slist
*curl_slist_new(const char *first
, ...) {
351 struct curl_slist
*l
;
357 l
= curl_slist_append(NULL
, first
);
364 struct curl_slist
*n
;
367 i
= va_arg(ap
, const char*);
371 n
= curl_slist_append(l
, i
);
374 curl_slist_free_all(l
);
385 int curl_header_strdup(const void *contents
, size_t sz
, const char *field
, char **value
) {
386 const char *p
= contents
;
394 if (memcmp(p
, field
, l
) != 0)
400 if (memchr(p
, 0, sz
))
403 /* Skip over preceeding whitespace */
404 while (sz
> 0 && strchr(WHITESPACE
, p
[0])) {
409 /* Truncate trailing whitespace*/
410 while (sz
> 0 && strchr(WHITESPACE
, p
[sz
-1]))
421 int curl_parse_http_time(const char *t
, usec_t
*ret
) {
430 loc
= newlocale(LC_TIME_MASK
, "C", (locale_t
) 0);
431 if (loc
== (locale_t
) 0)
435 e
= strptime_l(t
, "%a, %d %b %Y %H:%M:%S %Z", &tm
, loc
);
438 e
= strptime_l(t
, "%A, %d-%b-%y %H:%M:%S %Z", &tm
, loc
);
441 e
= strptime_l(t
, "%a %b %d %H:%M:%S %Y", &tm
, loc
);
447 if (v
== (time_t) -1)
450 *ret
= (usec_t
) v
* USEC_PER_SEC
;