26#include <zypp/base/Logger.h>
28#include <zypp-curl/parser/MetaLinkParser>
29#include <zypp/ManagedFile.h>
30#include <zypp-curl/private/curlhelper_p.h>
31#include <zypp-curl/auth/CurlAuthData>
36#undef CURLVERSION_AT_LEAST
37#define CURLVERSION_AT_LEAST(M,N,O) LIBCURL_VERSION_NUM >= ((((M)<<8)+(N))<<8)+(O)
46class multifetchrequest;
64 void adddnsfd(fd_set &rset,
int &maxfd);
90 static size_t _writefunction(
void *ptr,
size_t size,
size_t nmemb,
void *stream);
93 static size_t _headerfunction(
void *ptr,
size_t size,
size_t nmemb,
void *stream);
106#define WORKER_STARTING 0
107#define WORKER_LOOKUP 1
108#define WORKER_FETCH 2
109#define WORKER_DISCARD 3
111#define WORKER_SLEEP 5
112#define WORKER_BROKEN 6
121 void run(std::vector<Url> &urllist);
179 if (gettimeofday(&tv, NULL))
181 return tv.tv_sec + tv.tv_usec / 1000000.;
205 (void)curl_easy_getinfo(
_curl, CURLINFO_EFFECTIVE_URL, &effurl);
206 if (effurl && !strncasecmp(effurl,
"http", 4))
209 (void)curl_easy_getinfo(
_curl, CURLINFO_RESPONSE_CODE, &statuscode);
210 if (statuscode != 206)
257 if (l > 9 && !strncasecmp(p,
"Location:", 9))
259 std::string line(p + 9, l - 9);
260 if (line[l - 10] ==
'\r')
261 line.erase(l - 10, 1);
262 XXX <<
"#" <<
_workerno <<
": redirecting to" << line << endl;
265 if (l <= 14 || l >= 128 || strncasecmp(p,
"Content-Range:", 14) != 0)
269 while (l && (*p ==
' ' || *p ==
'\t'))
271 if (l < 6 || strncasecmp(p,
"bytes", 5))
278 unsigned long long start, off, filesize;
279 if (sscanf(buf,
"%llu-%llu/%llu", &start, &off, &filesize) != 3)
283 WAR <<
"#" <<
_workerno <<
": setting request filesize to " << filesize << endl;
290 XXX <<
"#" <<
_workerno <<
": filesize mismatch" << endl;
292 strncpy(
_curlError,
"filesize mismatch", CURL_ERROR_SIZE);
329 XXX <<
"reused worker from pool" << endl;
333 strncpy(
_curlError,
"curl_easy_init failed", CURL_ERROR_SIZE);
342 curl_easy_cleanup(
_curl);
345 strncpy(
_curlError,
"curl_easy_setopt failed", CURL_ERROR_SIZE);
348 curl_easy_setopt(
_curl, CURLOPT_PRIVATE,
this);
351 curl_easy_setopt(
_curl, CURLOPT_WRITEDATA,
this);
355 curl_easy_setopt(
_curl, CURLOPT_HEADERDATA,
this);
367 curl_easy_setopt(
_curl, CURLOPT_USERPWD,
_settings.userPassword().c_str());
368 std::string use_auth =
_settings.authType();
369 if (use_auth.empty())
370 use_auth =
"digest,basic";
371 long auth = CurlAuthData::auth_type_str2long(use_auth);
372 if( auth != CURLAUTH_NONE)
374 XXX <<
"#" <<
_workerno <<
": Enabling HTTP authentication methods: " << use_auth
375 <<
" (CURLOPT_HTTPAUTH=" << auth <<
")" << std::endl;
376 curl_easy_setopt(
_curl, CURLOPT_HTTPAUTH, auth);
391#if CURLVERSION_AT_LEAST(7,15,5)
392 curl_easy_setopt(
_curl, CURLOPT_MAX_RECV_SPEED_LARGE, (curl_off_t)0);
394 curl_easy_setopt(
_curl, CURLOPT_PRIVATE, (
void *)0);
395 curl_easy_setopt(
_curl, CURLOPT_WRITEFUNCTION, (
void *)0);
396 curl_easy_setopt(
_curl, CURLOPT_WRITEDATA, (
void *)0);
397 curl_easy_setopt(
_curl, CURLOPT_HEADERFUNCTION, (
void *)0);
398 curl_easy_setopt(
_curl, CURLOPT_HEADERDATA, (
void *)0);
402 curl_easy_cleanup(
_curl);
409 while (waitpid(
_pid, &status, 0) == -1)
426 const char *s = getenv(name.c_str());
427 return s && *s ? true :
false;
443 if (inet_pton(AF_INET, host.c_str(), addrbuf) == 1)
445 if (inet_pton(AF_INET6, host.c_str(), addrbuf) == 1)
456 if (schemeproxy !=
"http_proxy")
458 std::transform(schemeproxy.begin(), schemeproxy.end(), schemeproxy.begin(), ::toupper);
463 XXX <<
"checking DNS lookup of " << host << endl;
468 strncpy(
_curlError,
"DNS pipe creation failed", CURL_ERROR_SIZE);
472 if (
_pid == pid_t(-1))
478 strncpy(
_curlError,
"DNS checker fork failed", CURL_ERROR_SIZE);
485 struct addrinfo *ai, aihints;
486 memset(&aihints, 0,
sizeof(aihints));
487 aihints.ai_family = PF_UNSPEC;
488 int tstsock = socket(PF_INET6, SOCK_DGRAM | SOCK_CLOEXEC, 0);
490 aihints.ai_family = PF_INET;
493 aihints.ai_socktype = SOCK_STREAM;
494 aihints.ai_flags = AI_CANONNAME;
497 alarm(connecttimeout);
498 signal(SIGALRM, SIG_DFL);
499 if (getaddrinfo(host.c_str(), NULL, &aihints, &ai))
525 while (waitpid(
_pid, &status, 0) == -1)
536 if (!WIFEXITED(status))
539 strncpy(
_curlError,
"DNS lookup failed", CURL_ERROR_SIZE);
543 int exitcode = WEXITSTATUS(status);
544 XXX <<
"#" <<
_workerno <<
": DNS lookup returned " << exitcode << endl;
548 strncpy(
_curlError,
"DNS lookup failed", CURL_ERROR_SIZE);
578 size_t cnt = l >
sizeof(buf) ?
sizeof(buf) : l;
593 XXX <<
"start stealing!" << endl;
597 std::list<multifetchworker *>::iterator workeriter =
_request->
_workers.begin();
604 if (worker->
_pass == -1)
663 XXX <<
"#" <<
_workerno <<
": going to sleep for " << sl * 1000 <<
" ms" << endl;
684 std::list<multifetchworker *>::iterator workeriter =
_request->
_workers.begin();
703 const char *nochunk = getenv(
"ZYPP_NOCHUNK");
712 if (nochunk || !blklist)
762 sprintf(rangebuf,
"%llu-", (
unsigned long long)
_blkstart);
770 strncpy(
_curlError,
"curl_easy_setopt range failed", CURL_ERROR_SIZE);
777 strncpy(
_curlError,
"curl_multi_add_handle failed", CURL_ERROR_SIZE);
828 for (
size_t blkno = 0; blkno < blklist->numBlocks(); blkno++)
830 MediaBlock blk = blklist->getBlock(blkno);
834 else if (filesize != off_t(-1))
840 for (std::list<multifetchworker *>::iterator workeriter =
_workers.begin(); workeriter !=
_workers.end(); ++workeriter)
853 std::vector<Url>::iterator urliter = urllist.begin();
856 fd_set rset, wset, xset;
861 XXX <<
"finished!" << endl;
885 WAR <<
"No more active workers!" << endl;
887 for (std::list<multifetchworker *>::iterator workeriter =
_workers.begin(); workeriter !=
_workers.end(); ++workeriter)
900 curl_multi_fdset(
_multi, &rset, &wset, &xset, &maxfd);
903 for (std::list<multifetchworker *>::iterator workeriter =
_workers.begin(); workeriter !=
_workers.end(); ++workeriter)
904 (*workeriter)->adddnsfd(rset, maxfd);
915 for (std::list<multifetchworker *>::iterator workeriter =
_workers.begin(); workeriter !=
_workers.end(); ++workeriter)
931 tv.tv_usec = sl * 1000000;
933 int r = select(maxfd + 1, &rset, &wset, &xset, &tv);
934 if (r == -1 && errno != EINTR)
937 for (std::list<multifetchworker *>::iterator workeriter =
_workers.begin(); workeriter !=
_workers.end(); ++workeriter)
953 mcode = curl_multi_perform(
_multi, &tasks);
954 if (mcode == CURLM_CALL_MULTI_PERFORM)
956 if (mcode != CURLM_OK)
977 for (std::list<multifetchworker *>::iterator workeriter =
_workers.begin(); workeriter !=
_workers.end(); ++workeriter)
986 XXX <<
"#" << worker->
_workerno <<
": sleep done, wake up" << endl;
995 while ((msg = curl_multi_info_read(
_multi, &nqueue)) != 0)
997 if (msg->msg != CURLMSG_DONE)
999 CURL *easy = msg->easy_handle;
1000 CURLcode cc = msg->data.result;
1002 if (curl_easy_getinfo(easy, CURLINFO_PRIVATE, &worker) != CURLE_OK)
1012 curl_multi_remove_handle(
_multi, easy);
1013 if (cc == CURLE_HTTP_RETURNED_ERROR)
1015 long statuscode = 0;
1016 (void)curl_easy_getinfo(easy, CURLINFO_RESPONSE_CODE, &statuscode);
1017 XXX <<
"HTTP status " << statuscode << endl;
1018 if (statuscode == 416 && !
_blklist)
1024 XXX <<
"#" << worker->
_workerno <<
": retrying with no end range" << endl;
1044 WAR <<
"#" << worker->
_workerno <<
": checksum error, disable worker" << endl;
1046 strncpy(worker->
_curlError,
"checksum error", CURL_ERROR_SIZE);
1060 XXX <<
"#" << worker->
_workerno <<
": recheck checksum error, refetch block" << endl;
1073 int maxworkerno = 0;
1075 for (std::list<multifetchworker *>::iterator workeriter =
_workers.begin(); workeriter !=
_workers.end(); ++workeriter)
1090 double ratio = worker->
_avgspeed / maxavg;
1093 ratio = ratio * ratio;
1096 XXX <<
"#" << worker->
_workerno <<
": too slow ("<< ratio <<
", " << worker->
_avgspeed <<
", #" << maxworkerno <<
": " << maxavg <<
"), going to sleep for " << ratio * 1000 <<
" ms" << endl;
1117#if CURLVERSION_AT_LEAST(7,15,5)
1118 curl_easy_setopt(worker->
_curl, CURLOPT_MAX_RECV_SPEED_LARGE, (curl_off_t)(avg));
1160 WAR <<
"overall result" << endl;
1161 for (std::list<multifetchworker *>::iterator workeriter =
_workers.begin(); workeriter !=
_workers.end(); ++workeriter)
1171 if ( filesize == 0 )
return 2 * 1024 * 1024;
1172 else if ( filesize < 2*256*1024 )
return filesize;
1173 else if ( filesize < 8*1024*1024 )
return 256*1024;
1174 else if ( filesize < 256*1024*1024 )
return 1024*1024;
1184 MIL <<
"MediaMultiCurl::MediaMultiCurl(" << url_r <<
", " << attach_point_hint_r <<
")" << endl;
1198 curl_multi_cleanup(
_multi);
1201 std::map<std::string, CURL *>::iterator it;
1204 CURL *easy = it->second;
1207 curl_easy_cleanup(easy);
1223 for (; sl; sl = sl->next)
1232 while ((l = pread(fd, buf,
sizeof(buf) - 1, (off_t)0)) == -1 && errno == EINTR)
1238 while (*p ==
' ' || *p ==
'\t' || *p ==
'\r' || *p ==
'\n')
1240 if (!strncasecmp(p,
"<?xml", 5))
1242 while (*p && *p !=
'>')
1246 while (*p ==
' ' || *p ==
'\t' || *p ==
'\r' || *p ==
'\n')
1249 bool ret = !strncasecmp(p,
"<metalink", 9) ? true :
false;
1256 if ((fd = open(file.
asString().c_str(), O_RDONLY|O_CLOEXEC)) == -1)
1260 DBG <<
"looks_like_metalink(" << file <<
"): " << ret << endl;
1276 if ( curl_easy_getinfo(
_curl, CURLINFO_PRIVATE, &fp ) != CURLE_OK || !fp )
1278 if ( ftell( fp ) == 0 )
1283 long httpReturnCode = 0;
1284 if (curl_easy_getinfo(
_curl, CURLINFO_RESPONSE_CODE, &httpReturnCode ) != CURLE_OK || httpReturnCode == 0)
1288 bool ismetalink =
false;
1289 if (curl_easy_getinfo(
_curl, CURLINFO_CONTENT_TYPE, &ptr) == CURLE_OK && ptr)
1291 std::string ct = std::string(ptr);
1292 if (ct.find(
"application/metalink+xml") == 0 || ct.find(
"application/metalink4+xml") == 0)
1295 if (!ismetalink && dlnow < 256)
1304 DBG <<
"looks_like_metalink_fd: " << ismetalink << endl;
1321 if( assert_dir( dest.
dirname() ) )
1323 DBG <<
"assert_dir " << dest.
dirname() <<
" failed" << endl;
1333 ERR <<
"out of memory for temp file name" << endl;
1337 AutoFD tmp_fd { ::mkostemp( buf, O_CLOEXEC ) };
1340 ERR <<
"mkstemp failed for file '" << destNew <<
"'" << endl;
1345 file = ::fdopen( tmp_fd,
"we" );
1348 ERR <<
"fopen failed for file '" << destNew <<
"'" << endl;
1351 tmp_fd.resetDispose();
1354 DBG <<
"dest: " << dest << endl;
1355 DBG <<
"temp: " << destNew << endl;
1360 curl_easy_setopt(
_curl, CURLOPT_TIMECONDITION, CURL_TIMECOND_IFMODSINCE);
1361 curl_easy_setopt(
_curl, CURLOPT_TIMEVALUE, (
long)
PathInfo(target).mtime());
1365 curl_easy_setopt(
_curl, CURLOPT_TIMECONDITION, CURL_TIMECOND_NONE);
1366 curl_easy_setopt(
_curl, CURLOPT_TIMEVALUE, 0L);
1372 curl_easy_setopt(
_curl, CURLOPT_PRIVATE, (*file) );
1379 curl_easy_setopt(
_curl, CURLOPT_TIMECONDITION, CURL_TIMECOND_NONE);
1380 curl_easy_setopt(
_curl, CURLOPT_TIMEVALUE, 0L);
1382 curl_easy_setopt(
_curl, CURLOPT_PRIVATE, (
void *)0);
1385 curl_easy_setopt(
_curl, CURLOPT_TIMECONDITION, CURL_TIMECOND_NONE);
1386 curl_easy_setopt(
_curl, CURLOPT_TIMEVALUE, 0L);
1388 curl_easy_setopt(
_curl, CURLOPT_PRIVATE, (
void *)0);
1389 long httpReturnCode = 0;
1390 CURLcode infoRet = curl_easy_getinfo(
_curl, CURLINFO_RESPONSE_CODE, &httpReturnCode);
1391 if (infoRet == CURLE_OK)
1394 if ( httpReturnCode == 304
1397 DBG <<
"not modified: " <<
PathInfo(dest) << endl;
1403 WAR <<
"Could not get the response code." << endl;
1406 bool ismetalink =
false;
1409 if (curl_easy_getinfo(
_curl, CURLINFO_CONTENT_TYPE, &ptr) == CURLE_OK && ptr)
1411 std::string ct = std::string(ptr);
1412 if (ct.find(
"application/metalink+xml") == 0 || ct.find(
"application/metalink4+xml") == 0)
1427 bool userabort =
false;
1434 MediaBlockList bl = mlp.getBlockList();
1440 if ( !bl.haveFilesize() && ! srcFile.
downloadSize() ) {
1441 XXX <<
"No filesize in metalink file and no expected filesize, aborting multicurl." << std::endl;
1442 ZYPP_THROW( MediaException(
"Multicurl requires filesize but none was provided.") );
1445 std::vector<Url> urls = mlp.getUrls();
1451 ZYPP_THROW( MediaException(
"Multicurl enabled but not enough mirrors provided") );
1455 file = fopen((*destNew).c_str(),
"w+e");
1460 XXX <<
"reusing blocks from file " << target << endl;
1461 bl.reuseBlocks(file, target.
asString());
1464 if (bl.haveChecksum(1) &&
PathInfo(failedFile).isExist())
1466 XXX <<
"reusing blocks from file " << failedFile << endl;
1467 bl.reuseBlocks(file, failedFile.
asString());
1474 XXX <<
"reusing blocks from file " << df << endl;
1475 bl.reuseBlocks(file, df.
asString());
1482 catch (MediaCurlException &ex)
1484 userabort = ex.errstr() ==
"User abort";
1488 catch (MediaFileSizeExceededException &ex) {
1495 if (
PathInfo(destNew).size() >= 63336)
1497 ::unlink(failedFile.
asString().c_str());
1504 file = fopen((*destNew).c_str(),
"w+e");
1513 ERR <<
"Failed to chmod file " << destNew << endl;
1520 ERR <<
"Fclose failed for file '" << destNew <<
"'" << endl;
1524 if ( rename( destNew, dest ) != 0 )
1526 ERR <<
"Rename failed" << endl;
1529 destNew.resetDispose();
1537 if (blklist && filesize == off_t(-1) && blklist->haveFilesize())
1538 filesize = blklist->getFilesize();
1539 if (blklist && !blklist->haveBlocks() && filesize != 0)
1541 if (blklist && (filesize == 0 || !blklist->numBlocks()))
1550 _multi = curl_multi_init();
1564 std::vector<Url> myurllist;
1565 for (std::vector<Url>::iterator urliter = urllist->begin(); urliter != urllist->end(); ++urliter)
1569 std::string scheme = urliter->getScheme();
1570 if (scheme ==
"http" || scheme ==
"https" || scheme ==
"ftp" || scheme ==
"tftp")
1573 myurllist.push_back(internal::propagateQueryParams(*urliter,
_url));
1580 if (!myurllist.size())
1581 myurllist.push_back(baseurl);
1588 if (!blklist || !blklist->haveFileChecksum())
1590 if (fseeko(fp, off_t(0), SEEK_SET))
1591 ZYPP_THROW(MediaCurlException(
url,
"fseeko",
"seek error"));
1593 blklist->createFileDigest(dig);
1596 while ((l = fread(buf, 1,
sizeof(buf), fp)) > 0)
1598 if (!blklist->verifyFileDigest(dig))
1599 ZYPP_THROW(MediaCurlException(
url,
"file verification failed",
"checksum error"));
1604 return _dnsok.find(host) ==
_dnsok.end() ? false :
true;
1626 curl_easy_cleanup(oldeasy);
std::optional< KeyManagerCtx > _context
Reference counted access to a Tp object calling a custom Dispose function when the last AutoDispose h...
void resetDispose()
Set no dispose function.
Store and operate with byte count.
static const Unit MB
1000^2 Byte
std::string asString(unsigned field_width_r=0, unsigned unit_width_r=1) const
Auto selected Unit and precision.
Compute Message Digests (MD5, SHA1 etc)
bool update(const char *bytes, size_t len)
feed data into digest computation algorithm
Base class for Exception.
std::string getScheme() const
Returns the scheme name of the URL.
std::string asString() const
Returns a default string representation of the Url object.
std::string getHost(EEncoding eflag=zypp::url::E_DECODED) const
Returns the hostname or IP from the URL authority.
Pathname repoCachePath() const
Path where the caches are kept (/var/cache/zypp)
static ZConfig & instance()
Singleton ctor.
Wrapper class for stat/lstat.
Pathname extend(const std::string &r) const
Append string r to the last component of the path.
Pathname dirname() const
Return all but the last component od this path.
const std::string & asString() const
String representation.
Pathname absolutename() const
Return this path, adding a leading '/' if relative.
mode_t applyUmaskTo(mode_t mode_r)
Modify mode_r according to the current umask ( mode_r & ~getUmask() ).
int unlink(const Pathname &path)
Like 'unlink'.
int hardlinkCopy(const Pathname &oldpath, const Pathname &newpath)
Create newpath as hardlink or copy of oldpath.
std::string numstring(char n, int w=0)
Easy-to use interface to the ZYPP dependency resolver.
AutoDispose< const Pathname > ManagedFile
A Pathname plus associated cleanup code to be executed when path is no longer needed.
AutoDispose<int> calling ::close
AutoDispose<FILE*> calling ::fclose
#define ZYPP_RETHROW(EXCPT)
Drops a logline and rethrows, updating the CodeLocation.
#define ZYPP_THROW(EXCPT)
Drops a logline and throws the Exception.