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 "string-util.h"
23 #include "curl-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_INT(hashmap_get(g
->translate_fds
, INT_TO_PTR(fd
+1)));
50 assert(translated_fd
> 0);
53 if ((revents
& (EPOLLIN
|EPOLLOUT
)) == (EPOLLIN
|EPOLLOUT
))
54 action
= CURL_POLL_INOUT
;
55 else if (revents
& EPOLLIN
)
56 action
= CURL_POLL_IN
;
57 else if (revents
& EPOLLOUT
)
58 action
= CURL_POLL_OUT
;
62 if (curl_multi_socket_action(g
->curl
, translated_fd
, action
, &k
) < 0) {
63 log_debug("Failed to propagate IO event.");
67 curl_glue_check_finished(g
);
71 static int curl_glue_socket_callback(CURLM
*curl
, curl_socket_t s
, int action
, void *userdata
, void *socketp
) {
73 CurlGlue
*g
= userdata
;
80 io
= hashmap_get(g
->ios
, INT_TO_PTR(s
+1));
82 if (action
== CURL_POLL_REMOVE
) {
86 fd
= sd_event_source_get_io_fd(io
);
89 sd_event_source_set_enabled(io
, SD_EVENT_OFF
);
90 sd_event_source_unref(io
);
92 hashmap_remove(g
->ios
, INT_TO_PTR(s
+1));
93 hashmap_remove(g
->translate_fds
, INT_TO_PTR(fd
+1));
101 r
= hashmap_ensure_allocated(&g
->ios
, &trivial_hash_ops
);
107 r
= hashmap_ensure_allocated(&g
->translate_fds
, &trivial_hash_ops
);
113 if (action
== CURL_POLL_IN
)
115 else if (action
== CURL_POLL_OUT
)
117 else if (action
== CURL_POLL_INOUT
)
118 events
= EPOLLIN
|EPOLLOUT
;
121 if (sd_event_source_set_io_events(io
, events
) < 0)
124 if (sd_event_source_set_enabled(io
, SD_EVENT_ON
) < 0)
127 _cleanup_close_
int fd
= -1;
129 /* When curl needs to remove an fd from us it closes
130 * the fd first, and only then calls into us. This is
131 * nasty, since we cannot pass the fd on to epoll()
132 * anymore. Hence, duplicate the fds here, and keep a
133 * copy for epoll which we control after use. */
135 fd
= fcntl(s
, F_DUPFD_CLOEXEC
, 3);
139 if (sd_event_add_io(g
->event
, &io
, fd
, events
, curl_glue_on_io
, g
) < 0)
142 sd_event_source_set_description(io
, "curl-io");
144 r
= hashmap_put(g
->ios
, INT_TO_PTR(s
+1), io
);
147 sd_event_source_unref(io
);
151 r
= hashmap_put(g
->translate_fds
, INT_TO_PTR(fd
+1), INT_TO_PTR(s
+1));
154 hashmap_remove(g
->ios
, INT_TO_PTR(s
+1));
155 sd_event_source_unref(io
);
165 static int curl_glue_on_timer(sd_event_source
*s
, uint64_t usec
, void *userdata
) {
166 CurlGlue
*g
= userdata
;
172 if (curl_multi_socket_action(g
->curl
, CURL_SOCKET_TIMEOUT
, 0, &k
) != CURLM_OK
) {
173 log_debug("Failed to propagate timeout.");
177 curl_glue_check_finished(g
);
181 static int curl_glue_timer_callback(CURLM
*curl
, long timeout_ms
, void *userdata
) {
182 CurlGlue
*g
= userdata
;
188 if (timeout_ms
< 0) {
190 if (sd_event_source_set_enabled(g
->timer
, SD_EVENT_OFF
) < 0)
197 usec
= now(clock_boottime_or_monotonic()) + (usec_t
) timeout_ms
* USEC_PER_MSEC
+ USEC_PER_MSEC
- 1;
200 if (sd_event_source_set_time(g
->timer
, usec
) < 0)
203 if (sd_event_source_set_enabled(g
->timer
, SD_EVENT_ONESHOT
) < 0)
206 if (sd_event_add_time(g
->event
, &g
->timer
, clock_boottime_or_monotonic(), usec
, 0, curl_glue_on_timer
, g
) < 0)
209 sd_event_source_set_description(g
->timer
, "curl-timer");
215 CurlGlue
*curl_glue_unref(CurlGlue
*g
) {
222 curl_multi_cleanup(g
->curl
);
224 while ((io
= hashmap_steal_first(g
->ios
))) {
227 fd
= sd_event_source_get_io_fd(io
);
230 hashmap_remove(g
->translate_fds
, INT_TO_PTR(fd
+1));
233 sd_event_source_unref(io
);
236 hashmap_free(g
->ios
);
238 sd_event_source_unref(g
->timer
);
239 sd_event_unref(g
->event
);
245 int curl_glue_new(CurlGlue
**glue
, sd_event
*event
) {
246 _cleanup_(curl_glue_unrefp
) CurlGlue
*g
= NULL
;
249 g
= new0(CurlGlue
, 1);
254 g
->event
= sd_event_ref(event
);
256 r
= sd_event_default(&g
->event
);
261 g
->curl
= curl_multi_init();
265 if (curl_multi_setopt(g
->curl
, CURLMOPT_SOCKETDATA
, g
) != CURLM_OK
)
268 if (curl_multi_setopt(g
->curl
, CURLMOPT_SOCKETFUNCTION
, curl_glue_socket_callback
) != CURLM_OK
)
271 if (curl_multi_setopt(g
->curl
, CURLMOPT_TIMERDATA
, g
) != CURLM_OK
)
274 if (curl_multi_setopt(g
->curl
, CURLMOPT_TIMERFUNCTION
, curl_glue_timer_callback
) != CURLM_OK
)
283 int curl_glue_make(CURL
**ret
, const char *url
, void *userdata
) {
284 const char *useragent
;
291 c
= curl_easy_init();
295 /* curl_easy_setopt(c, CURLOPT_VERBOSE, 1L); */
297 if (curl_easy_setopt(c
, CURLOPT_URL
, url
) != CURLE_OK
) {
302 if (curl_easy_setopt(c
, CURLOPT_PRIVATE
, userdata
) != CURLE_OK
) {
307 useragent
= strjoina(program_invocation_short_name
, "/" PACKAGE_VERSION
);
308 if (curl_easy_setopt(c
, CURLOPT_USERAGENT
, useragent
) != CURLE_OK
) {
313 if (curl_easy_setopt(c
, CURLOPT_FOLLOWLOCATION
, 1L) != CURLE_OK
) {
322 curl_easy_cleanup(c
);
326 int curl_glue_add(CurlGlue
*g
, CURL
*c
) {
330 if (curl_multi_add_handle(g
->curl
, c
) != CURLM_OK
)
336 void curl_glue_remove_and_free(CurlGlue
*g
, CURL
*c
) {
343 curl_multi_remove_handle(g
->curl
, c
);
345 curl_easy_cleanup(c
);
348 struct curl_slist
*curl_slist_new(const char *first
, ...) {
349 struct curl_slist
*l
;
355 l
= curl_slist_append(NULL
, first
);
362 struct curl_slist
*n
;
365 i
= va_arg(ap
, const char*);
369 n
= curl_slist_append(l
, i
);
372 curl_slist_free_all(l
);
383 int curl_header_strdup(const void *contents
, size_t sz
, const char *field
, char **value
) {
384 const char *p
= contents
;
392 if (memcmp(p
, field
, l
) != 0)
398 if (memchr(p
, 0, sz
))
401 /* Skip over preceeding whitespace */
402 while (sz
> 0 && strchr(WHITESPACE
, p
[0])) {
407 /* Truncate trailing whitespace*/
408 while (sz
> 0 && strchr(WHITESPACE
, p
[sz
-1]))
419 int curl_parse_http_time(const char *t
, usec_t
*ret
) {
428 loc
= newlocale(LC_TIME_MASK
, "C", (locale_t
) 0);
429 if (loc
== (locale_t
) 0)
433 e
= strptime_l(t
, "%a, %d %b %Y %H:%M:%S %Z", &tm
, loc
);
436 e
= strptime_l(t
, "%A, %d-%b-%y %H:%M:%S %Z", &tm
, loc
);
439 e
= strptime_l(t
, "%a %b %d %H:%M:%S %Y", &tm
, loc
);
445 if (v
== (time_t) -1)
448 *ret
= (usec_t
) v
* USEC_PER_SEC
;