summaryrefslogtreecommitdiff
path: root/lib/libalpm/dload.c
diff options
context:
space:
mode:
Diffstat (limited to 'lib/libalpm/dload.c')
-rw-r--r--lib/libalpm/dload.c155
1 files changed, 118 insertions, 37 deletions
diff --git a/lib/libalpm/dload.c b/lib/libalpm/dload.c
index a3980d65..97778c27 100644
--- a/lib/libalpm/dload.c
+++ b/lib/libalpm/dload.c
@@ -1,7 +1,7 @@
/*
* download.c
*
- * Copyright (c) 2006-2011 Pacman Development Team <pacman-dev@archlinux.org>
+ * Copyright (c) 2006-2012 Pacman Development Team <pacman-dev@archlinux.org>
* Copyright (c) 2002-2006 by Judd Vinet <jvinet@zeroflux.org>
*
* This program is free software; you can redistribute it and/or modify
@@ -23,11 +23,19 @@
#include <errno.h>
#include <string.h>
#include <unistd.h>
+#include <sys/socket.h> /* setsockopt, SO_KEEPALIVE */
#include <sys/time.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <signal.h>
+#ifdef HAVE_NETINET_IN_H
+#include <netinet/in.h> /* IPPROTO_TCP */
+#endif
+#ifdef HAVE_NETINET_TCP_H
+#include <netinet/tcp.h> /* TCP_KEEPINTVL, TCP_KEEPIDLE */
+#endif
+
#ifdef HAVE_LIBCURL
#include <curl/curl.h>
#endif
@@ -82,7 +90,7 @@ static void inthandler(int UNUSED signum)
dload_interrupted = ABORT_SIGINT;
}
-static int curl_progress(void *file, double dltotal, double dlnow,
+static int dload_progress_cb(void *file, double dltotal, double dlnow,
double UNUSED ultotal, double UNUSED ulnow)
{
struct dload_payload *payload = (struct dload_payload *)file;
@@ -184,7 +192,7 @@ static mode_t _getumask(void)
return mask;
}
-static size_t parse_headers(void *ptr, size_t size, size_t nmemb, void *user)
+static size_t dload_parseheader_cb(void *ptr, size_t size, size_t nmemb, void *user)
{
size_t realsize = size * nmemb;
const char *fptr, *endptr = NULL;
@@ -215,6 +223,47 @@ static size_t parse_headers(void *ptr, size_t size, size_t nmemb, void *user)
return realsize;
}
+static int dload_sockopt_cb(void *userdata, curl_socket_t curlfd,
+ curlsocktype purpose)
+{
+ alpm_handle_t *handle = userdata;
+ int optval = 1;
+
+ /* this whole method is to prevent FTP control connections from going sour
+ * during a long data transfer; crappy firewalls love to drop otherwise idle
+ * connections if there is no traffic. */
+ if(purpose != CURLSOCKTYPE_IPCXN) {
+ return 0;
+ }
+
+ /* don't abort operation if any setsockopt fails, just log to debug */
+ if(setsockopt(curlfd, SOL_SOCKET, SO_KEEPALIVE, (void *)&optval,
+ sizeof(optval)) < 0) {
+ _alpm_log(handle, ALPM_LOG_DEBUG,
+ "Failed to set SO_KEEPALIVE on fd %d\n", curlfd);
+ }
+ else {
+#ifdef TCP_KEEPIDLE
+ optval = 60;
+ if(setsockopt(curlfd, IPPROTO_TCP, TCP_KEEPIDLE, (void *)&optval,
+ sizeof(optval)) < 0) {
+ _alpm_log(handle, ALPM_LOG_DEBUG,
+ "Failed to set TCP_KEEPIDLE on fd %d\n", curlfd);
+ }
+#endif
+#ifdef TCP_KEEPINTVL
+ optval = 60;
+ if(setsockopt(curlfd, IPPROTO_TCP, TCP_KEEPINTVL, (void *)&optval,
+ sizeof(optval)) < 0) {
+ _alpm_log(handle, ALPM_LOG_DEBUG,
+ "Failed to set TCP_KEEPINTVL on fd %d\n", curlfd);
+ }
+#endif
+ }
+
+ return 0;
+}
+
static void curl_set_handle_opts(struct dload_payload *payload,
CURL *curl, char *error_buffer)
{
@@ -232,13 +281,15 @@ static void curl_set_handle_opts(struct dload_payload *payload,
curl_easy_setopt(curl, CURLOPT_FILETIME, 1L);
curl_easy_setopt(curl, CURLOPT_NOPROGRESS, 0L);
curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1L);
- curl_easy_setopt(curl, CURLOPT_PROGRESSFUNCTION, curl_progress);
+ curl_easy_setopt(curl, CURLOPT_PROGRESSFUNCTION, dload_progress_cb);
curl_easy_setopt(curl, CURLOPT_PROGRESSDATA, (void *)payload);
curl_easy_setopt(curl, CURLOPT_LOW_SPEED_LIMIT, 1024L);
curl_easy_setopt(curl, CURLOPT_LOW_SPEED_TIME, 10L);
- curl_easy_setopt(curl, CURLOPT_HEADERFUNCTION, parse_headers);
+ curl_easy_setopt(curl, CURLOPT_HEADERFUNCTION, dload_parseheader_cb);
curl_easy_setopt(curl, CURLOPT_WRITEHEADER, (void *)payload);
curl_easy_setopt(curl, CURLOPT_NETRC, CURL_NETRC_OPTIONAL);
+ curl_easy_setopt(curl, CURLOPT_SOCKOPTFUNCTION, dload_sockopt_cb);
+ curl_easy_setopt(curl, CURLOPT_SOCKOPTDATA, (void *)handle);
_alpm_log(handle, ALPM_LOG_DEBUG, "url: %s\n", payload->fileurl);
@@ -388,6 +439,8 @@ static int curl_download_internal(struct dload_payload *payload,
/* perform transfer */
payload->curlerr = curl_easy_perform(curl);
+ _alpm_log(handle, ALPM_LOG_DEBUG, "curl returned error %d from transfer\n",
+ payload->curlerr);
/* disconnect relationships from the curl handle for things that might go out
* of scope, but could still be touched on connection teardown. This really
@@ -558,6 +611,22 @@ int _alpm_download(struct dload_payload *payload, const char *localpath,
}
}
+static char *filecache_find_url(alpm_handle_t *handle, const char *url)
+{
+ const char *basename = strrchr(url, '/');
+
+ if(basename == NULL) {
+ return NULL;
+ }
+
+ basename++;
+ if(basename == '\0') {
+ return NULL;
+ }
+
+ return _alpm_filecache_find(handle, basename);
+}
+
/** Fetch a remote pkg. */
char SYMEXPORT *alpm_fetch_pkgurl(alpm_handle_t *handle, const char *url)
{
@@ -565,7 +634,7 @@ char SYMEXPORT *alpm_fetch_pkgurl(alpm_handle_t *handle, const char *url)
const char *cachedir;
char *final_file = NULL;
struct dload_payload payload;
- int ret;
+ int ret = 0;
CHECK_HANDLE(handle, return NULL);
ASSERT(url, RET_ERR(handle, ALPM_ERR_WRONG_ARGS, NULL));
@@ -574,51 +643,63 @@ char SYMEXPORT *alpm_fetch_pkgurl(alpm_handle_t *handle, const char *url)
cachedir = _alpm_filecache_setup(handle);
memset(&payload, 0, sizeof(struct dload_payload));
- payload.handle = handle;
- STRDUP(payload.fileurl, url, RET_ERR(handle, ALPM_ERR_MEMORY, NULL));
- payload.allow_resume = 1;
-
- /* download the file */
- ret = _alpm_download(&payload, cachedir, &final_file);
- _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;
+
+ /* 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;
+
+ /* download the file */
+ ret = _alpm_download(&payload, cachedir, &final_file);
+ _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;
+ }
+ _alpm_log(handle, ALPM_LOG_DEBUG, "successfully downloaded %s\n", url);
}
- _alpm_log(handle, ALPM_LOG_DEBUG, "successfully downloaded %s\n", url);
/* attempt to download the signature */
if(ret == 0 && (handle->siglevel & ALPM_SIG_PACKAGE)) {
- char *sig_final_file = NULL;
+ char *sig_filepath, *sig_final_file = NULL;
size_t len;
len = strlen(url) + 5;
MALLOC(payload.fileurl, len, RET_ERR(handle, ALPM_ERR_MEMORY, NULL));
snprintf(payload.fileurl, len, "%s.sig", url);
- payload.handle = handle;
- 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);
- 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);
+
+ sig_filepath = filecache_find_url(handle, payload.fileurl);
+ if(sig_filepath == NULL) {
+ payload.handle = handle;
+ 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);
+ 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);
+ }
+ FREE(sig_final_file);
}
- FREE(sig_final_file);
+ free(sig_filepath);
_alpm_dload_payload_reset(&payload);
}
/* we should be able to find the file the second time around */
- filepath = _alpm_filecache_find(handle, final_file);
+ if(filepath == NULL) {
+ filepath = _alpm_filecache_find(handle, final_file);
+ }
free(final_file);
return filepath;