#include #include #include #include #include #include #include #include #include #include #include #define URIS_S 2 const char *uris[] = { "https://api.turris.cz/updater-defs/3.5/omnia/dev-karel/base.lua", "https://api.turris.cz/updater-defs/3.5/omnia/dev-karel/base.lua.sig" }; CURLM *curl_multi; struct event_base *e_base; struct event *e_timer; struct dwn_data { const char *err; }; static void check() { CURLMsg *msg; int msgs_left; struct dwn_data *data; char *url; while ((msg = curl_multi_info_read(curl_multi, &msgs_left))) { if (msg->msg != CURLMSG_DONE) continue; // No other message types are defined in libcurl. We check just because of compatibility with possible future versions. curl_easy_getinfo(msg->easy_handle, CURLINFO_PRIVATE, &data); curl_easy_getinfo(msg->easy_handle, CURLINFO_EFFECTIVE_URL, &url); if (msg->data.result == CURLE_OK) { printf("Download succesfull (%s)\n", url); } else { printf("Download failed (%s): %s\n", url, data->err); exit(2); } } } static size_t clb_write(char *ptr, size_t size, size_t nmemb, void *userd) { // Drop all data return size * nmemb; } static void clb_timer(int fd __attribute__((unused)), short kind __attribute__((unused)), void *userp) { printf("Timer callback\n"); int running = 0; curl_multi_socket_action(curl_multi, CURL_SOCKET_TIMEOUT, 0, &running); check(); } static int clb_timer_set(CURLM *curl_multi __attribute__((unused)), long timeout_ms, void *userp) { printf("Timer set %ld\n", timeout_ms); struct timeval timeout; timeout.tv_sec = timeout_ms / 1000; timeout.tv_usec = (timeout_ms % 1000) * 1000; evtimer_add(e_timer, &timeout); return 0; } struct dt_s { size_t id; struct event *e; }; static void clb_socket(int fd, short kind, void *userp) { printf("Socket callback\n"); int action = ((kind & EV_READ) ? CURL_CSELECT_IN : 0) | ((kind & EV_WRITE) ? CURL_CSELECT_OUT : 0); int running = 0; curl_multi_socket_action(curl_multi, fd, action, &running); check(); if (running <= 0 && evtimer_pending(e_timer, NULL)) evtimer_del(e_timer); } size_t id_next = 0; static int clb_socket_set(CURL *curl_easy, curl_socket_t s, int what, void *userp, void *socketp) { struct dt_s *sdata = socketp; if (what == CURL_POLL_REMOVE) { printf("Socket remove %ld\n", sdata->id); event_free(sdata->e); free(sdata); } else { if (!sdata) { // New socket. No data associated. printf("Socket new %ld\n", id_next); sdata = malloc(sizeof *sdata); sdata->e = NULL; sdata->id = id_next++; curl_multi_assign(curl_multi, s, sdata); } else printf("Socket change %ld\n", sdata->id); short kind = ((what & CURL_POLL_IN) ? EV_READ : 0) | ((what & CURL_POLL_OUT) ? EV_WRITE : 0) | EV_PERSIST; if (sdata->e) { event_del(sdata->e); event_assign(sdata->e, e_base, s, kind, clb_socket, sdata); } else sdata->e = event_new(e_base, s, kind, clb_socket, sdata); event_add(sdata->e, NULL); } return 0; } static CURL *new_curl(const char *uri, bool simple) { CURL *curl = curl_easy_init(); curl_easy_setopt(curl, CURLOPT_VERBOSE, 1); curl_easy_setopt(curl, CURLOPT_URL, uri); curl_easy_setopt(curl, CURLOPT_ACCEPT_ENCODING, ""); curl_easy_setopt(curl, CURLOPT_CAINFO, "./ca.pem"); curl_easy_setopt(curl, CURLOPT_CRLFILE, "./crl.pem"); curl_easy_setopt(curl, CURLOPT_FAILONERROR, 1); curl_easy_setopt(curl, CURLOPT_TIMEOUT, 120); curl_easy_setopt(curl, CURLOPT_CONNECTTIMEOUT, 30); curl_easy_setopt(curl, CURLOPT_CAPATH, NULL); curl_easy_setopt(curl, CURLOPT_SSL_CIPHER_LIST, NULL); if (!simple) { struct dwn_data *data = malloc(sizeof *data); curl_multi_add_handle(curl_multi, curl); curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, clb_write); curl_easy_setopt(curl, CURLOPT_ERRORBUFFER, data->err); curl_easy_setopt(curl, CURLOPT_PRIVATE, data); } return curl; } int main(int argc, char *argv[]) { bool simple = (argc > 1 && !strcmp(argv[1], "-s")); bool multi = (argc > 1 && !strcmp(argv[1], "-m")); curl_global_init(CURL_GLOBAL_SSL); if (!simple) { struct event_config *config = event_config_new(); event_config_require_features(config, EV_FEATURE_FDS); event_config_set_flag(config, EVENT_BASE_FLAG_NOLOCK); e_base = event_base_new_with_config(config); event_config_free(config); e_timer = evtimer_new(e_base, clb_timer, NULL); curl_multi = curl_multi_init(); curl_multi_setopt(curl_multi, CURLMOPT_MAX_TOTAL_CONNECTIONS, 1); curl_multi_setopt(curl_multi, CURLMOPT_SOCKETFUNCTION, clb_socket_set); curl_multi_setopt(curl_multi, CURLMOPT_TIMERFUNCTION, clb_timer_set); } size_t i; CURL *curls[URIS_S]; for (i = 0; i < URIS_S; i++) { curls[i] = new_curl(uris[i], simple); } if (simple) { for (i = 0; i < URIS_S; i++) { CURLcode res = curl_easy_perform(curls[i]); if (res == CURLE_OK) printf("Download succesfull (%s)\n", uris[i]); else printf("Download failed (%s): %s\n", uris[i], curl_easy_strerror(res)); } } else if (multi) { int sr = 0; CURLMcode cd; do { cd = curl_multi_perform(curl_multi, &sr); } while(sr > 0 || cd == CURLM_CALL_MULTI_PERFORM); if (cd != CURLM_OK) { printf("Download failed: %s\n", curl_multi_strerror(cd)); exit(3); } else printf("Download succesfull\n"); } else event_base_dispatch(e_base); for (i = 0; i < URIS_S; i++) { if (!simple) curl_multi_remove_handle(curl_multi, curls[i]); curl_easy_cleanup(curls[i]); } if (!simple) { curl_multi_cleanup(curl_multi); event_free(e_timer); event_base_free(e_base); } curl_global_cleanup(); return 0; }