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 "curl-util.h"
24 static void curl_glue_check_finished(CurlGlue *g) {
30 msg = curl_multi_info_read(g->curl, &k);
34 if (msg->msg != CURLMSG_DONE)
38 g->on_finished(g, msg->easy_handle, msg->data.result);
41 static int curl_glue_on_io(sd_event_source *s, int fd, uint32_t revents, void *userdata) {
42 CurlGlue *g = userdata;
43 int action, k = 0, translated_fd;
48 translated_fd = PTR_TO_INT(hashmap_get(g->translate_fds, INT_TO_PTR(fd+1)));
49 assert(translated_fd > 0);
52 if ((revents & (EPOLLIN|EPOLLOUT)) == (EPOLLIN|EPOLLOUT))
53 action = CURL_POLL_INOUT;
54 else if (revents & EPOLLIN)
55 action = CURL_POLL_IN;
56 else if (revents & EPOLLOUT)
57 action = CURL_POLL_OUT;
61 if (curl_multi_socket_action(g->curl, translated_fd, action, &k) < 0) {
62 log_debug("Failed to propagate IO event.");
66 curl_glue_check_finished(g);
70 static int curl_glue_socket_callback(CURLM *curl, curl_socket_t s, int action, void *userdata, void *socketp) {
72 CurlGlue *g = userdata;
79 io = hashmap_get(g->ios, INT_TO_PTR(s+1));
81 if (action == CURL_POLL_REMOVE) {
85 fd = sd_event_source_get_io_fd(io);
88 sd_event_source_set_enabled(io, SD_EVENT_OFF);
89 sd_event_source_unref(io);
91 hashmap_remove(g->ios, INT_TO_PTR(s+1));
92 hashmap_remove(g->translate_fds, INT_TO_PTR(fd+1));
100 r = hashmap_ensure_allocated(&g->ios, &trivial_hash_ops);
106 r = hashmap_ensure_allocated(&g->translate_fds, &trivial_hash_ops);
112 if (action == CURL_POLL_IN)
114 else if (action == CURL_POLL_OUT)
116 else if (action == CURL_POLL_INOUT)
117 events = EPOLLIN|EPOLLOUT;
120 if (sd_event_source_set_io_events(io, events) < 0)
123 if (sd_event_source_set_enabled(io, SD_EVENT_ON) < 0)
126 _cleanup_close_ int fd = -1;
128 /* When curl needs to remove an fd from us it closes
129 * the fd first, and only then calls into us. This is
130 * nasty, since we cannot pass the fd on to epoll()
131 * anymore. Hence, duplicate the fds here, and keep a
132 * copy for epoll which we control after use. */
134 fd = fcntl(s, F_DUPFD_CLOEXEC, 3);
138 if (sd_event_add_io(g->event, &io, fd, events, curl_glue_on_io, g) < 0)
141 sd_event_source_set_description(io, "curl-io");
143 r = hashmap_put(g->ios, INT_TO_PTR(s+1), io);
146 sd_event_source_unref(io);
150 r = hashmap_put(g->translate_fds, INT_TO_PTR(fd+1), INT_TO_PTR(s+1));
153 hashmap_remove(g->ios, INT_TO_PTR(s+1));
154 sd_event_source_unref(io);
164 static int curl_glue_on_timer(sd_event_source *s, uint64_t usec, void *userdata) {
165 CurlGlue *g = userdata;
171 if (curl_multi_socket_action(g->curl, CURL_SOCKET_TIMEOUT, 0, &k) != CURLM_OK) {
172 log_debug("Failed to propagate timeout.");
176 curl_glue_check_finished(g);
180 static int curl_glue_timer_callback(CURLM *curl, long timeout_ms, void *userdata) {
181 CurlGlue *g = userdata;
187 if (timeout_ms < 0) {
189 if (sd_event_source_set_enabled(g->timer, SD_EVENT_OFF) < 0)
196 usec = now(clock_boottime_or_monotonic()) + (usec_t) timeout_ms * USEC_PER_MSEC + USEC_PER_MSEC - 1;
199 if (sd_event_source_set_time(g->timer, usec) < 0)
202 if (sd_event_source_set_enabled(g->timer, SD_EVENT_ONESHOT) < 0)
205 if (sd_event_add_time(g->event, &g->timer, clock_boottime_or_monotonic(), usec, 0, curl_glue_on_timer, g) < 0)
208 sd_event_source_set_description(g->timer, "curl-timer");
214 CurlGlue *curl_glue_unref(CurlGlue *g) {
221 curl_multi_cleanup(g->curl);
223 while ((io = hashmap_steal_first(g->ios))) {
226 fd = sd_event_source_get_io_fd(io);
229 hashmap_remove(g->translate_fds, INT_TO_PTR(fd+1));
232 sd_event_source_unref(io);
235 hashmap_free(g->ios);
237 sd_event_source_unref(g->timer);
238 sd_event_unref(g->event);
244 int curl_glue_new(CurlGlue **glue, sd_event *event) {
245 _cleanup_(curl_glue_unrefp) CurlGlue *g = NULL;
248 g = new0(CurlGlue, 1);
253 g->event = sd_event_ref(event);
255 r = sd_event_default(&g->event);
260 g->curl = curl_multi_init();
264 if (curl_multi_setopt(g->curl, CURLMOPT_SOCKETDATA, g) != CURLM_OK)
267 if (curl_multi_setopt(g->curl, CURLMOPT_SOCKETFUNCTION, curl_glue_socket_callback) != CURLM_OK)
270 if (curl_multi_setopt(g->curl, CURLMOPT_TIMERDATA, g) != CURLM_OK)
273 if (curl_multi_setopt(g->curl, CURLMOPT_TIMERFUNCTION, curl_glue_timer_callback) != CURLM_OK)
282 int curl_glue_make(CURL **ret, const char *url, void *userdata) {
283 const char *useragent;
290 c = curl_easy_init();
294 /* curl_easy_setopt(c, CURLOPT_VERBOSE, 1L); */
296 if (curl_easy_setopt(c, CURLOPT_URL, url) != CURLE_OK) {
301 if (curl_easy_setopt(c, CURLOPT_PRIVATE, userdata) != CURLE_OK) {
306 useragent = strappenda(program_invocation_short_name, "/" PACKAGE_VERSION);
307 if (curl_easy_setopt(c, CURLOPT_USERAGENT, useragent) != CURLE_OK) {
312 if (curl_easy_setopt(c, CURLOPT_FOLLOWLOCATION, 1L) != CURLE_OK) {
321 curl_easy_cleanup(c);
325 int curl_glue_add(CurlGlue *g, CURL *c) {
329 if (curl_multi_add_handle(g->curl, c) != CURLM_OK)
335 void curl_glue_remove_and_free(CurlGlue *g, CURL *c) {
342 curl_multi_remove_handle(g->curl, c);
344 curl_easy_cleanup(c);
347 struct curl_slist *curl_slist_new(const char *first, ...) {
348 struct curl_slist *l;
354 l = curl_slist_append(NULL, first);
361 struct curl_slist *n;
364 i = va_arg(ap, const char*);
368 n = curl_slist_append(l, i);
371 curl_slist_free_all(l);
382 int curl_header_strdup(const void *contents, size_t sz, const char *field, char **value) {
383 const char *p = contents;
391 if (memcmp(p, field, l) != 0)
397 if (memchr(p, 0, sz))
400 /* Skip over preceeding whitespace */
401 while (sz > 0 && strchr(WHITESPACE, p[0])) {
406 /* Truncate trailing whitespace*/
407 while (sz > 0 && strchr(WHITESPACE, p[sz-1]))
418 int curl_parse_http_time(const char *t, usec_t *ret) {
427 loc = newlocale(LC_TIME_MASK, "C", (locale_t) 0);
428 if (loc == (locale_t) 0)
432 e = strptime_l(t, "%a, %d %b %Y %H:%M:%S %Z", &tm, loc);
435 e = strptime_l(t, "%A, %d-%b-%y %H:%M:%S %Z", &tm, loc);
438 e = strptime_l(t, "%a %b %d %H:%M:%S %Y", &tm, loc);
444 if (v == (time_t) -1)
447 *ret = (usec_t) v * USEC_PER_SEC;