[pacman-dev] [PATCH] Convert '-U pkg1 pkg2' codepath to parallel download
Anatol Pomozov
anatol.pomozov at gmail.com
Mon May 11 08:50:10 UTC 2020
Installing remote packages using its URL is an interesting case for ALPM
API. Unlike package sync ('pacman -S pkg1 pkg2') '-U' does not deal with
server mirror list. Thus _alpm_multi_download() should be able to
handle file download for payloads that either have 'fileurl' field
or pair of fields ('servers' and 'filepath') set.
Signature for alpm_fetch_pkgurl() has changed and it accepts an
output list that is populated with filepaths to fetched packages.
Signed-off-by: Anatol Pomozov <anatol.pomozov at gmail.com>
---
lib/libalpm/alpm.h | 11 ++-
lib/libalpm/dload.c | 204 +++++++++++++++++++++++++------------------
lib/libalpm/dload.h | 7 +-
src/pacman/upgrade.c | 102 ++++++++++------------
4 files changed, 181 insertions(+), 143 deletions(-)
diff --git a/lib/libalpm/alpm.h b/lib/libalpm/alpm.h
index 3ea66ccc..c6a13273 100644
--- a/lib/libalpm/alpm.h
+++ b/lib/libalpm/alpm.h
@@ -755,12 +755,15 @@ typedef void (*alpm_cb_totaldl)(off_t total);
typedef int (*alpm_cb_fetch)(const char *url, const char *localpath,
int force);
-/** Fetch a remote pkg.
+/** Fetch a list of remote packages.
* @param handle the context handle
- * @param url URL of the package to download
- * @return the downloaded filepath on success, NULL on error
+ * @param urls list of package URLs to download
+ * @param fetched list of filepaths to the fetched packages, each item
+ * corresponds to one in `urls` list
+ * @return 0 on success or -1 on failure
*/
-char *alpm_fetch_pkgurl(alpm_handle_t *handle, const char *url);
+int alpm_fetch_pkgurl(alpm_handle_t *handle, const alpm_list_t *urls,
+ alpm_list_t **fetched);
/** @addtogroup alpm_api_options Options
* Libalpm option getters and setters
diff --git a/lib/libalpm/dload.c b/lib/libalpm/dload.c
index 13aa4086..43fe9847 100644
--- a/lib/libalpm/dload.c
+++ b/lib/libalpm/dload.c
@@ -613,7 +613,9 @@ static int curl_multi_retry_next_server(CURLM *curlm, CURL *curl, struct dload_p
size_t len;
alpm_handle_t *handle = payload->handle;
- payload->servers = payload->servers->next;
+ if(payload->servers) {
+ payload->servers = payload->servers->next;
+ }
if(!payload->servers) {
_alpm_log(payload->handle, ALPM_LOG_DEBUG,
"%s: no more servers to retry\n", payload->remote_name);
@@ -622,7 +624,7 @@ static int curl_multi_retry_next_server(CURLM *curlm, CURL *curl, struct dload_p
server = payload->servers->data;
/* regenerate a new fileurl */
- free(payload->fileurl);
+ FREE(payload->fileurl);
len = strlen(server) + strlen(payload->filepath) + 2;
MALLOC(payload->fileurl, len, RET_ERR(handle, ALPM_ERR_MEMORY, -1));
snprintf(payload->fileurl, len, "%s/%s", server, payload->filepath);
@@ -849,20 +851,28 @@ static int curl_multi_add_payload(alpm_handle_t *handle, CURLM *curlm,
struct dload_payload *payload, const char *localpath)
{
size_t len;
- const char *server;
CURL *curl = NULL;
char hostname[HOSTNAME_SIZE];
int ret = -1;
- ASSERT(payload->servers, RET_ERR(handle, ALPM_ERR_SERVER_NONE, -1));
- server = payload->servers->data;
-
curl = curl_easy_init();
payload->curl = curl;
- len = strlen(server) + strlen(payload->filepath) + 2;
- MALLOC(payload->fileurl, len, GOTO_ERR(handle, ALPM_ERR_MEMORY, cleanup));
- snprintf(payload->fileurl, len, "%s/%s", server, payload->filepath);
+ if(payload->fileurl) {
+ ASSERT(!payload->servers, GOTO_ERR(handle, ALPM_ERR_WRONG_ARGS, cleanup));
+ ASSERT(!payload->filepath, GOTO_ERR(handle, ALPM_ERR_WRONG_ARGS, cleanup));
+ } else {
+ const char *server;
+
+ ASSERT(payload->servers, GOTO_ERR(handle, ALPM_ERR_SERVER_NONE, cleanup));
+ ASSERT(payload->filepath, GOTO_ERR(handle, ALPM_ERR_WRONG_ARGS, cleanup));
+
+ server = payload->servers->data;
+
+ len = strlen(server) + strlen(payload->filepath) + 2;
+ MALLOC(payload->fileurl, len, GOTO_ERR(handle, ALPM_ERR_MEMORY, cleanup));
+ snprintf(payload->fileurl, len, "%s/%s", server, payload->filepath);
+ }
payload->tempfile_openmode = "wb";
if(!payload->remote_name) {
@@ -1046,22 +1056,29 @@ int _alpm_multi_download(alpm_handle_t *handle,
alpm_list_t *s;
int success = 0;
- for(s = payload->servers; s; s = s->next) {
- const char *server = s->data;
- char *fileurl;
- int ret;
-
- size_t len = strlen(server) + strlen(payload->filepath) + 2;
- MALLOC(fileurl, len, RET_ERR(handle, ALPM_ERR_MEMORY, -1));
- snprintf(fileurl, len, "%s/%s", server, payload->filepath);
-
- ret = handle->fetchcb(fileurl, localpath, payload->force);
- free(fileurl);
-
- if (ret != -1) {
+ if(payload->fileurl) {
+ if (handle->fetchcb(payload->fileurl, localpath, payload->force) != -1) {
success = 1;
break;
}
+ } else {
+ for(s = payload->servers; s; s = s->next) {
+ const char *server = s->data;
+ char *fileurl;
+ int ret;
+
+ size_t len = strlen(server) + strlen(payload->filepath) + 2;
+ MALLOC(fileurl, len, RET_ERR(handle, ALPM_ERR_MEMORY, -1));
+ snprintf(fileurl, len, "%s/%s", server, payload->filepath);
+
+ ret = handle->fetchcb(fileurl, localpath, payload->force);
+ free(fileurl);
+
+ if (ret != -1) {
+ success = 1;
+ break;
+ }
+ }
}
if(!success && !payload->errors_ok) {
RET_ERR(handle, ALPM_ERR_EXTERNAL_DOWNLOAD, -1);
@@ -1087,82 +1104,101 @@ static char *filecache_find_url(alpm_handle_t *handle, const char *url)
return _alpm_filecache_find(handle, filebase);
}
-char SYMEXPORT *alpm_fetch_pkgurl(alpm_handle_t *handle, const char *url)
+int SYMEXPORT alpm_fetch_pkgurl(alpm_handle_t *handle, const alpm_list_t *urls,
+ alpm_list_t **fetched)
{
- char *filepath;
- const char *cachedir, *final_pkg_url = NULL;
- char *final_file = NULL;
- struct dload_payload payload = {0};
- int ret = 0;
+ const char *cachedir;
+ alpm_list_t *payloads = NULL;
+ int download_sigs;
+ const alpm_list_t *i;
+ alpm_event_t event;
- CHECK_HANDLE(handle, return NULL);
- ASSERT(url, RET_ERR(handle, ALPM_ERR_WRONG_ARGS, NULL));
+ CHECK_HANDLE(handle, return -1);
+ download_sigs = handle->siglevel & ALPM_SIG_PACKAGE;
/* find a valid cache dir to download to */
cachedir = _alpm_filecache_setup(handle);
- /* attempt to find the file in our pkgcache */
- filepath = filecache_find_url(handle, url);
- if(filepath == NULL) {
- STRDUP(payload.fileurl, url, RET_ERR(handle, ALPM_ERR_MEMORY, NULL));
- payload.allow_resume = 1;
- payload.handle = handle;
- payload.trust_remote_name = 1;
-
- /* download the file */
- ret = _alpm_download(&payload, cachedir, &final_file, &final_pkg_url);
- _alpm_dload_payload_reset(&payload);
- if(ret == -1) {
- _alpm_log(handle, ALPM_LOG_WARNING, _("failed to download %s\n"), url);
- free(final_file);
- return NULL;
+ for(i = urls; i; i = i->next) {
+ char *url = i->data;
+
+ /* attempt to find the file in our pkgcache */
+ char *filepath = filecache_find_url(handle, url);
+ if(filepath) {
+ /* the file is locally cached so add it to the output right away */
+ alpm_list_append(fetched, filepath);
+ } else {
+ struct dload_payload *payload = NULL;
+
+ ASSERT(url, GOTO_ERR(handle, ALPM_ERR_WRONG_ARGS, err));
+ CALLOC(payload, 1, sizeof(*payload), GOTO_ERR(handle, ALPM_ERR_MEMORY, err));
+ STRDUP(payload->fileurl, url, FREE(payload); GOTO_ERR(handle, ALPM_ERR_MEMORY, err));
+ payload->allow_resume = 1;
+ payload->handle = handle;
+ payload->trust_remote_name = 1;
+ payloads = alpm_list_add(payloads, payload);
+
+ if(download_sigs) {
+ int len = strlen(url) + 5;
+ CALLOC(payload, 1, sizeof(*payload), GOTO_ERR(handle, ALPM_ERR_MEMORY, err));
+ payload->signature = 1;
+ MALLOC(payload->fileurl, len, FREE(payload); GOTO_ERR(handle, ALPM_ERR_MEMORY, err));
+ snprintf(payload->fileurl, len, "%s.sig", url);
+ payload->handle = handle;
+ payload->trust_remote_name = 1;
+ payload->errors_ok = (handle->siglevel & ALPM_SIG_PACKAGE_OPTIONAL);
+ /* set hard upper limit of 16KiB */
+ payload->max_size = 16 * 1024;
+ payloads = alpm_list_add(payloads, payload);
+ }
}
- _alpm_log(handle, ALPM_LOG_DEBUG, "successfully downloaded %s\n", url);
}
- /* attempt to download the signature */
- if(ret == 0 && final_pkg_url && (handle->siglevel & ALPM_SIG_PACKAGE)) {
- char *sig_filepath, *sig_final_file = NULL;
- size_t len;
-
- len = strlen(final_pkg_url) + 5;
- MALLOC(payload.fileurl, len, free(final_file); RET_ERR(handle, ALPM_ERR_MEMORY, NULL));
- snprintf(payload.fileurl, len, "%s.sig", final_pkg_url);
-
- sig_filepath = filecache_find_url(handle, payload.fileurl);
- if(sig_filepath == NULL) {
- payload.signature = 1;
- payload.handle = handle;
- payload.trust_remote_name = 1;
- payload.force = 1;
- payload.errors_ok = (handle->siglevel & ALPM_SIG_PACKAGE_OPTIONAL);
-
- /* set hard upper limit of 16KiB */
- payload.max_size = 16 * 1024;
-
- ret = _alpm_download(&payload, cachedir, &sig_final_file, NULL);
- if(ret == -1 && !payload.errors_ok) {
- _alpm_log(handle, ALPM_LOG_WARNING,
- _("failed to download %s\n"), payload.fileurl);
- /* Warn now, but don't return NULL. We will fail later during package
- * load time. */
- } else if(ret == 0) {
- _alpm_log(handle, ALPM_LOG_DEBUG,
- "successfully downloaded %s\n", payload.fileurl);
+ if(payloads) {
+ event.type = ALPM_EVENT_PKG_RETRIEVE_START;
+ EVENT(handle, &event);
+ if(_alpm_multi_download(handle, payloads, cachedir) == -1) {
+ _alpm_log(handle, ALPM_LOG_WARNING, _("failed to retrieve some files\n"));
+ event.type = ALPM_EVENT_PKG_RETRIEVE_FAILED;
+ EVENT(handle, &event);
+
+ GOTO_ERR(handle, ALPM_ERR_RETRIEVE, err);
+ } else {
+ event.type = ALPM_EVENT_PKG_RETRIEVE_DONE;
+ EVENT(handle, &event);
+ }
+
+ for(i = payloads; i; i = i->next) {
+ struct dload_payload *payload = i->data;
+ const char *filename;
+ char *filepath;
+
+ if(payload->signature) {
+ continue;
+ }
+
+ filename = mbasename(payload->destfile_name);
+ filepath = _alpm_filecache_find(handle, filename);
+ if(filepath) {
+ alpm_list_append(fetched, filepath);
+ } else {
+ _alpm_log(handle, ALPM_LOG_WARNING, _("download completed successfully but no file in the cache\n"));
+ GOTO_ERR(handle, ALPM_ERR_RETRIEVE, err);
}
- FREE(sig_final_file);
}
- free(sig_filepath);
- _alpm_dload_payload_reset(&payload);
- }
- /* we should be able to find the file the second time around */
- if(filepath == NULL) {
- filepath = _alpm_filecache_find(handle, final_file);
+ alpm_list_free_inner(payloads, (alpm_list_fn_free)_alpm_dload_payload_reset);
+ FREELIST(payloads);
}
- free(final_file);
- return filepath;
+ return 0;
+
+err:
+ alpm_list_free_inner(payloads, (alpm_list_fn_free)_alpm_dload_payload_reset);
+ FREELIST(payloads);
+ FREELIST(*fetched);
+
+ return -1;
}
void _alpm_dload_payload_reset(struct dload_payload *payload)
diff --git a/lib/libalpm/dload.h b/lib/libalpm/dload.h
index 1e4af755..f119fc2e 100644
--- a/lib/libalpm/dload.h
+++ b/lib/libalpm/dload.h
@@ -30,9 +30,14 @@ struct dload_payload {
char *tempfile_name;
char *destfile_name;
char *content_disp_name;
+ /* client has to provide either
+ * 1) fileurl - full URL to the file
+ * 2) pair of (servers, filepath), in this case ALPM iterates over the
+ * server list and tries to download "$server/$filepath"
+ */
char *fileurl;
- char *filepath; /* download URL path */
alpm_list_t *servers;
+ char *filepath; /* download URL path */
long respcode;
off_t initial_size;
off_t max_size;
diff --git a/src/pacman/upgrade.c b/src/pacman/upgrade.c
index 5f984e44..840e3a31 100644
--- a/src/pacman/upgrade.c
+++ b/src/pacman/upgrade.c
@@ -30,6 +30,34 @@
#include "conf.h"
#include "util.h"
+/* add targets to the created transaction */
+static int load_packages(alpm_list_t *targets, int siglevel)
+{
+ alpm_list_t *i;
+ int retval = 0;
+
+ for(i = targets; i; i = alpm_list_next(i)) {
+ const char *targ = i->data;
+ alpm_pkg_t *pkg;
+
+ if(alpm_pkg_load(config->handle, targ, 1, siglevel, &pkg) != 0) {
+ pm_printf(ALPM_LOG_ERROR, "'%s': %s\n",
+ targ, alpm_strerror(alpm_errno(config->handle)));
+ retval = 1;
+ continue;
+ }
+ if(alpm_add_pkg(config->handle, pkg) == -1) {
+ pm_printf(ALPM_LOG_ERROR, "'%s': %s\n",
+ targ, alpm_strerror(alpm_errno(config->handle)));
+ alpm_pkg_free(pkg);
+ retval = 1;
+ continue;
+ }
+ config->explicit_adds = alpm_list_add(config->explicit_adds, pkg);
+ }
+ return retval;
+}
+
/**
* @brief Upgrade a specified list of packages.
*
@@ -39,43 +67,30 @@
*/
int pacman_upgrade(alpm_list_t *targets)
{
- int retval = 0, *file_is_remote;
+ int retval = 0;
+ alpm_list_t *remote_targets = NULL, *fetched_files = NULL;
+ alpm_list_t *local_targets = NULL;
alpm_list_t *i;
- unsigned int n, num_targets;
if(targets == NULL) {
pm_printf(ALPM_LOG_ERROR, _("no targets specified (use -h for help)\n"));
return 1;
}
- num_targets = alpm_list_count(targets);
-
- /* Check for URL targets and process them */
- file_is_remote = malloc(num_targets * sizeof(int));
- if(file_is_remote == NULL) {
- pm_printf(ALPM_LOG_ERROR, _("memory exhausted\n"));
- return 1;
- }
-
- for(i = targets, n = 0; i; i = alpm_list_next(i), n++) {
+ /* carve out remote targets and move it into a separate list */
+ for(i = targets; i; i = alpm_list_next(i)) {
if(strstr(i->data, "://")) {
- char *str = alpm_fetch_pkgurl(config->handle, i->data);
- if(str == NULL) {
- pm_printf(ALPM_LOG_ERROR, "'%s': %s\n",
- (char *)i->data, alpm_strerror(alpm_errno(config->handle)));
- retval = 1;
- } else {
- free(i->data);
- i->data = str;
- file_is_remote[n] = 1;
- }
+ remote_targets = alpm_list_add(remote_targets, i->data);
} else {
- file_is_remote[n] = 0;
+ local_targets = alpm_list_add(local_targets, i->data);
}
}
- if(retval) {
- goto fail_free;
+ if(remote_targets) {
+ retval = alpm_fetch_pkgurl(config->handle, remote_targets, &fetched_files);
+ if(retval) {
+ goto fail_free;
+ }
}
/* Step 1: create a new transaction */
@@ -85,39 +100,16 @@ int pacman_upgrade(alpm_list_t *targets)
}
printf(_("loading packages...\n"));
- /* add targets to the created transaction */
- for(i = targets, n = 0; i; i = alpm_list_next(i), n++) {
- const char *targ = i->data;
- alpm_pkg_t *pkg;
- int siglevel;
-
- if(file_is_remote[n]) {
- siglevel = alpm_option_get_remote_file_siglevel(config->handle);
- } else {
- siglevel = alpm_option_get_local_file_siglevel(config->handle);
- }
-
- if(alpm_pkg_load(config->handle, targ, 1, siglevel, &pkg) != 0) {
- pm_printf(ALPM_LOG_ERROR, "'%s': %s\n",
- targ, alpm_strerror(alpm_errno(config->handle)));
- retval = 1;
- continue;
- }
- if(alpm_add_pkg(config->handle, pkg) == -1) {
- pm_printf(ALPM_LOG_ERROR, "'%s': %s\n",
- targ, alpm_strerror(alpm_errno(config->handle)));
- alpm_pkg_free(pkg);
- retval = 1;
- continue;
- }
- config->explicit_adds = alpm_list_add(config->explicit_adds, pkg);
- }
+ retval |= load_packages(local_targets, alpm_option_get_local_file_siglevel(config->handle));
+ retval |= load_packages(fetched_files, alpm_option_get_remote_file_siglevel(config->handle));
if(retval) {
goto fail_release;
}
- free(file_is_remote);
+ alpm_list_free(remote_targets);
+ alpm_list_free(local_targets);
+ FREELIST(fetched_files);
/* now that targets are resolved, we can hand it all off to the sync code */
return sync_prepare_execute();
@@ -125,7 +117,9 @@ int pacman_upgrade(alpm_list_t *targets)
fail_release:
trans_release();
fail_free:
- free(file_is_remote);
+ alpm_list_free(remote_targets);
+ alpm_list_free(local_targets);
+ FREELIST(fetched_files);
return retval;
}
--
2.26.2
More information about the pacman-dev
mailing list