#include "downloadpool.h" #include "macros.h" #include #include #include #include struct l2_dlpool__handle { CURL *easy; void *user; struct l2_dlpool__handle *next; struct l2_dlpool__handle *prev; }; struct l2_dlpool__pool_tag { CURLM *multi; struct l2_dlpool__handle *head, *tail; l2_dlpool_download_init_proc_t *initproc; l2_dlpool_one_downloaded_proc_t *dlproc; }; l2_dlpool_t *l2_dlpool_init(l2_dlpool_download_init_proc_t *init, l2_dlpool_one_downloaded_proc_t *dlproc) { l2_dlpool_t *ret = calloc(1, sizeof(l2_dlpool_t)); if (!ret) return NULL; ret->multi = curl_multi_init(); if (!ret->multi) { goto cleanup; } ret->initproc = init; ret->dlproc = dlproc; return ret; cleanup: if (ret) { if (ret->multi) curl_multi_cleanup(ret->multi); } free(ret); return NULL; } int l2_dlpool_add(l2_dlpool_t *pool, void *user) { int res = -1; struct l2_dlpool__handle *newhandle = calloc(1, sizeof(struct l2_dlpool__handle)); if (!newhandle) return -1; newhandle->easy = curl_easy_init(); if (!newhandle->easy) goto cleanup; curl_easy_setopt(newhandle->easy, CURLOPT_USERAGENT, L2_USER_AGENT); if ((*pool->initproc)(newhandle->easy, user) < 0) { res = 0; goto cleanup; } newhandle->user = user; CURLMcode mc = curl_multi_add_handle(pool->multi, newhandle->easy); if (mc != CURLM_OK) { if (mc == CURLM_OUT_OF_MEMORY) { fputs("CURL out of memory. (You are doomed.)\n", stderr); abort(); } goto cleanup; } if (pool->tail) { pool->tail->next = newhandle; newhandle->prev = pool->tail; pool->tail = newhandle; } else { pool->head = pool->tail = newhandle; } return 1; cleanup: if (newhandle) { if (newhandle->easy) curl_easy_cleanup(newhandle->easy); } free(newhandle); return res; } int l2_dlpool__download_complete(l2_dlpool_t *pool, CURL *easy, CURLcode code) { struct l2_dlpool__handle *handle = NULL; int uret = 0; for (struct l2_dlpool__handle *cur = pool->head; cur; cur = cur->next) { if (cur->easy == easy) { handle = cur; continue; } } if (handle) { uret = (*pool->dlproc)(easy, code, handle->user); if (handle->prev) { handle->prev->next = handle->next; } else { pool->head = handle->next; } if (handle->next) { handle->next->prev = handle->prev; } else { pool->tail = handle->prev; } free(handle); handle = NULL; } curl_multi_remove_handle(pool->multi, easy); curl_easy_cleanup(easy); if (uret < 0) return -1; return 0; } int l2_dlpool_perform(l2_dlpool_t *pool) { CURLMcode code; int running = 0; int queuelen = 0; CURLMsg *msg; do { code = curl_multi_perform(pool->multi, &running); if (code != CURLM_OK) { return -1; } while ((msg = curl_multi_info_read(pool->multi, &queuelen))) { if (msg->msg == CURLMSG_DONE) { if (l2_dlpool__download_complete(pool, msg->easy_handle, msg->data.result) < 0) return -1; } } code = curl_multi_poll(pool->multi, NULL, 0, 1000, NULL); if (code != CURLM_OK) { return -1; } } while (running); return 0; } void l2_dlpool_cleanup(l2_dlpool_t *pool) { if (!pool) return; /* assumes pool->multi is valid (API must not return a handle where this isn't the case) */ /* curl docs say to remove handles BEFORE ANY HANDLES ARE CLEANED UP */ for (struct l2_dlpool__handle *cur = pool->head; cur; cur = cur->next) { curl_multi_remove_handle(pool->multi, cur->easy); /* assume it succeeds (if it fails, too bad we tried LOL) */ } for (struct l2_dlpool__handle *cur = pool->head, *temp; cur; cur = temp) { temp = cur->next; curl_easy_cleanup(cur->easy); free(cur); } curl_multi_cleanup(pool->multi); free(pool); }