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);
243 int curl_glue_new(CurlGlue **glue, sd_event *event) {
244 _cleanup_(curl_glue_unrefp) CurlGlue *g = NULL;
247 g = new0(CurlGlue, 1);
252 g->event = sd_event_ref(event);
254 r = sd_event_default(&g->event);
259 g->curl = curl_multi_init();
263 if (curl_multi_setopt(g->curl, CURLMOPT_SOCKETDATA, g) != CURLM_OK)
266 if (curl_multi_setopt(g->curl, CURLMOPT_SOCKETFUNCTION, curl_glue_socket_callback) != CURLM_OK)
269 if (curl_multi_setopt(g->curl, CURLMOPT_TIMERDATA, g) != CURLM_OK)
272 if (curl_multi_setopt(g->curl, CURLMOPT_TIMERFUNCTION, curl_glue_timer_callback) != CURLM_OK)
281 int curl_glue_make(CURL **ret, const char *url, void *userdata) {
282 const char *useragent;
289 c = curl_easy_init();
293 /* curl_easy_setopt(c, CURLOPT_VERBOSE, 1L); */
295 if (curl_easy_setopt(c, CURLOPT_URL, url) != CURLE_OK) {
300 if (curl_easy_setopt(c, CURLOPT_PRIVATE, userdata) != CURLE_OK) {
305 useragent = strappenda(program_invocation_short_name, "/" PACKAGE_VERSION);
306 if (curl_easy_setopt(c, CURLOPT_USERAGENT, useragent) != CURLE_OK) {
311 if (curl_easy_setopt(c, CURLOPT_FOLLOWLOCATION, 1L) != CURLE_OK) {
320 curl_easy_cleanup(c);
324 int curl_glue_add(CurlGlue *g, CURL *c) {
328 if (curl_multi_add_handle(g->curl, c) != CURLM_OK)
334 void curl_glue_remove_and_free(CurlGlue *g, CURL *c) {
341 curl_multi_remove_handle(g->curl, c);
343 curl_easy_cleanup(c);
346 struct curl_slist *curl_slist_new(const char *first, ...) {
347 struct curl_slist *l;
353 l = curl_slist_append(NULL, first);
360 struct curl_slist *n;
363 i = va_arg(ap, const char*);
367 n = curl_slist_append(l, i);
370 curl_slist_free_all(l);
381 int curl_header_strdup(const void *contents, size_t sz, const char *field, char **value) {
382 const char *p = contents;
390 if (memcmp(p, field, l) != 0)
396 if (memchr(p, 0, sz))
399 /* Skip over preceeding whitespace */
400 while (sz > 0 && strchr(WHITESPACE, p[0])) {
405 /* Truncate trailing whitespace*/
406 while (sz > 0 && strchr(WHITESPACE, p[sz-1]))