2 This file is part of systemd.
4 Copyright 2014 Lennart Poettering
6 systemd is free software; you can redistribute it and/or modify it
7 under the terms of the GNU Lesser General Public License as published by
8 the Free Software Foundation; either version 2.1 of the License, or
9 (at your option) any later version.
11 systemd is distributed in the hope that it will be useful, but
12 WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 Lesser General Public License for more details.
16 You should have received a copy of the GNU Lesser General Public License
17 along with systemd; If not, see <http://www.gnu.org/licenses/>.
20 #include "alloc-util.h"
21 #include "curl-util.h"
23 #include "string-util.h"
25 static void curl_glue_check_finished(CurlGlue
*g
) {
31 msg
= curl_multi_info_read(g
->curl
, &k
);
35 if (msg
->msg
!= CURLMSG_DONE
)
39 g
->on_finished(g
, msg
->easy_handle
, msg
->data
.result
);
42 static int curl_glue_on_io(sd_event_source
*s
, int fd
, uint32_t revents
, void *userdata
) {
43 CurlGlue
*g
= userdata
;
44 int action
, k
= 0, translated_fd
;
49 translated_fd
= PTR_TO_FD(hashmap_get(g
->translate_fds
, FD_TO_PTR(fd
)));
51 if ((revents
& (EPOLLIN
|EPOLLOUT
)) == (EPOLLIN
|EPOLLOUT
))
52 action
= CURL_POLL_INOUT
;
53 else if (revents
& EPOLLIN
)
54 action
= CURL_POLL_IN
;
55 else if (revents
& EPOLLOUT
)
56 action
= CURL_POLL_OUT
;
60 if (curl_multi_socket_action(g
->curl
, translated_fd
, action
, &k
) < 0) {
61 log_debug("Failed to propagate IO event.");
65 curl_glue_check_finished(g
);
69 static int curl_glue_socket_callback(CURLM
*curl
, curl_socket_t s
, int action
, void *userdata
, void *socketp
) {
71 CurlGlue
*g
= userdata
;
78 io
= hashmap_get(g
->ios
, FD_TO_PTR(s
));
80 if (action
== CURL_POLL_REMOVE
) {
84 fd
= sd_event_source_get_io_fd(io
);
87 sd_event_source_set_enabled(io
, SD_EVENT_OFF
);
88 sd_event_source_unref(io
);
90 hashmap_remove(g
->ios
, FD_TO_PTR(s
));
91 hashmap_remove(g
->translate_fds
, FD_TO_PTR(fd
));
99 r
= hashmap_ensure_allocated(&g
->ios
, &trivial_hash_ops
);
105 r
= hashmap_ensure_allocated(&g
->translate_fds
, &trivial_hash_ops
);
111 if (action
== CURL_POLL_IN
)
113 else if (action
== CURL_POLL_OUT
)
115 else if (action
== CURL_POLL_INOUT
)
116 events
= EPOLLIN
|EPOLLOUT
;
119 if (sd_event_source_set_io_events(io
, events
) < 0)
122 if (sd_event_source_set_enabled(io
, SD_EVENT_ON
) < 0)
125 _cleanup_close_
int fd
= -1;
127 /* When curl needs to remove an fd from us it closes
128 * the fd first, and only then calls into us. This is
129 * nasty, since we cannot pass the fd on to epoll()
130 * anymore. Hence, duplicate the fds here, and keep a
131 * copy for epoll which we control after use. */
133 fd
= fcntl(s
, F_DUPFD_CLOEXEC
, 3);
137 if (sd_event_add_io(g
->event
, &io
, fd
, events
, curl_glue_on_io
, g
) < 0)
140 (void) sd_event_source_set_description(io
, "curl-io");
142 r
= hashmap_put(g
->ios
, FD_TO_PTR(s
), io
);
145 sd_event_source_unref(io
);
149 r
= hashmap_put(g
->translate_fds
, FD_TO_PTR(fd
), FD_TO_PTR(s
));
152 hashmap_remove(g
->ios
, FD_TO_PTR(s
));
153 sd_event_source_unref(io
);
163 static int curl_glue_on_timer(sd_event_source
*s
, uint64_t usec
, void *userdata
) {
164 CurlGlue
*g
= userdata
;
170 if (curl_multi_socket_action(g
->curl
, CURL_SOCKET_TIMEOUT
, 0, &k
) != CURLM_OK
) {
171 log_debug("Failed to propagate timeout.");
175 curl_glue_check_finished(g
);
179 static int curl_glue_timer_callback(CURLM
*curl
, long timeout_ms
, void *userdata
) {
180 CurlGlue
*g
= userdata
;
186 if (timeout_ms
< 0) {
188 if (sd_event_source_set_enabled(g
->timer
, SD_EVENT_OFF
) < 0)
195 usec
= now(clock_boottime_or_monotonic()) + (usec_t
) timeout_ms
* USEC_PER_MSEC
+ USEC_PER_MSEC
- 1;
198 if (sd_event_source_set_time(g
->timer
, usec
) < 0)
201 if (sd_event_source_set_enabled(g
->timer
, SD_EVENT_ONESHOT
) < 0)
204 if (sd_event_add_time(g
->event
, &g
->timer
, clock_boottime_or_monotonic(), usec
, 0, curl_glue_on_timer
, g
) < 0)
207 (void) sd_event_source_set_description(g
->timer
, "curl-timer");
213 CurlGlue
*curl_glue_unref(CurlGlue
*g
) {
220 curl_multi_cleanup(g
->curl
);
222 while ((io
= hashmap_steal_first(g
->ios
))) {
225 fd
= sd_event_source_get_io_fd(io
);
228 hashmap_remove(g
->translate_fds
, FD_TO_PTR(fd
));
231 sd_event_source_unref(io
);
234 hashmap_free(g
->ios
);
236 sd_event_source_unref(g
->timer
);
237 sd_event_unref(g
->event
);
241 int curl_glue_new(CurlGlue
**glue
, sd_event
*event
) {
242 _cleanup_(curl_glue_unrefp
) CurlGlue
*g
= NULL
;
245 g
= new0(CurlGlue
, 1);
250 g
->event
= sd_event_ref(event
);
252 r
= sd_event_default(&g
->event
);
257 g
->curl
= curl_multi_init();
261 if (curl_multi_setopt(g
->curl
, CURLMOPT_SOCKETDATA
, g
) != CURLM_OK
)
264 if (curl_multi_setopt(g
->curl
, CURLMOPT_SOCKETFUNCTION
, curl_glue_socket_callback
) != CURLM_OK
)
267 if (curl_multi_setopt(g
->curl
, CURLMOPT_TIMERDATA
, g
) != CURLM_OK
)
270 if (curl_multi_setopt(g
->curl
, CURLMOPT_TIMERFUNCTION
, curl_glue_timer_callback
) != CURLM_OK
)
279 int curl_glue_make(CURL
**ret
, const char *url
, void *userdata
) {
280 const char *useragent
;
287 c
= curl_easy_init();
291 /* curl_easy_setopt(c, CURLOPT_VERBOSE, 1L); */
293 if (curl_easy_setopt(c
, CURLOPT_URL
, url
) != CURLE_OK
) {
298 if (curl_easy_setopt(c
, CURLOPT_PRIVATE
, userdata
) != CURLE_OK
) {
303 useragent
= strjoina(program_invocation_short_name
, "/" PACKAGE_VERSION
);
304 if (curl_easy_setopt(c
, CURLOPT_USERAGENT
, useragent
) != CURLE_OK
) {
309 if (curl_easy_setopt(c
, CURLOPT_FOLLOWLOCATION
, 1L) != CURLE_OK
) {
318 curl_easy_cleanup(c
);
322 int curl_glue_add(CurlGlue
*g
, CURL
*c
) {
326 if (curl_multi_add_handle(g
->curl
, c
) != CURLM_OK
)
332 void curl_glue_remove_and_free(CurlGlue
*g
, CURL
*c
) {
339 curl_multi_remove_handle(g
->curl
, c
);
341 curl_easy_cleanup(c
);
344 struct curl_slist
*curl_slist_new(const char *first
, ...) {
345 struct curl_slist
*l
;
351 l
= curl_slist_append(NULL
, first
);
358 struct curl_slist
*n
;
361 i
= va_arg(ap
, const char*);
365 n
= curl_slist_append(l
, i
);
368 curl_slist_free_all(l
);
379 int curl_header_strdup(const void *contents
, size_t sz
, const char *field
, char **value
) {
380 const char *p
= contents
;
388 if (memcmp(p
, field
, l
) != 0)
394 if (memchr(p
, 0, sz
))
397 /* Skip over preceeding whitespace */
398 while (sz
> 0 && strchr(WHITESPACE
, p
[0])) {
403 /* Truncate trailing whitespace*/
404 while (sz
> 0 && strchr(WHITESPACE
, p
[sz
-1]))
415 int curl_parse_http_time(const char *t
, usec_t
*ret
) {
424 loc
= newlocale(LC_TIME_MASK
, "C", (locale_t
) 0);
425 if (loc
== (locale_t
) 0)
429 e
= strptime_l(t
, "%a, %d %b %Y %H:%M:%S %Z", &tm
, loc
);
432 e
= strptime_l(t
, "%A, %d-%b-%y %H:%M:%S %Z", &tm
, loc
);
435 e
= strptime_l(t
, "%a %b %d %H:%M:%S %Y", &tm
, loc
);
441 if (v
== (time_t) -1)
444 *ret
= (usec_t
) v
* USEC_PER_SEC
;