X-Git-Url: https://git.saurik.com/apt.git/blobdiff_plain/e8afd16892e87a6e2f17c1019ee455f5583387c2..a6ae3d3df490e7a5a1c8324ba9dc2e63972b1529:/apt-pkg/acquire-worker.cc diff --git a/apt-pkg/acquire-worker.cc b/apt-pkg/acquire-worker.cc index 55fa5734f..a4fbc7651 100644 --- a/apt-pkg/acquire-worker.cc +++ b/apt-pkg/acquire-worker.cc @@ -23,10 +23,10 @@ #include #include +#include #include #include #include -#include #include #include @@ -34,9 +34,7 @@ #include #include #include -#include -#include -#include +#include #include /*}}}*/ @@ -44,34 +42,16 @@ using namespace std; // Worker::Worker - Constructor for Queue startup /*{{{*/ -// --------------------------------------------------------------------- -/* */ -pkgAcquire::Worker::Worker(Queue *Q,MethodConfig *Cnf, - pkgAcquireStatus *log) : Log(log) +pkgAcquire::Worker::Worker(Queue *Q, MethodConfig *Cnf, pkgAcquireStatus *log) : + d(NULL), OwnerQ(Q), Log(log), Config(Cnf), Access(Cnf->Access), + CurrentItem(nullptr), CurrentSize(0), TotalSize(0) { - OwnerQ = Q; - Config = Cnf; - Access = Cnf->Access; - CurrentItem = 0; - TotalSize = 0; - CurrentSize = 0; - Construct(); } /*}}}*/ // Worker::Worker - Constructor for method config startup /*{{{*/ -// --------------------------------------------------------------------- -/* */ -pkgAcquire::Worker::Worker(MethodConfig *Cnf) +pkgAcquire::Worker::Worker(MethodConfig *Cnf) : Worker(nullptr, Cnf, nullptr) { - OwnerQ = 0; - Config = Cnf; - Access = Cnf->Access; - CurrentItem = 0; - TotalSize = 0; - CurrentSize = 0; - - Construct(); } /*}}}*/ // Worker::Construct - Constructor helper /*{{{*/ @@ -113,17 +93,38 @@ pkgAcquire::Worker::~Worker() bool pkgAcquire::Worker::Start() { // Get the method path - string Method = _config->FindDir("Dir::Bin::Methods") + Access; + constexpr char const * const methodsDir = "Dir::Bin::Methods"; + std::string const confItem = std::string(methodsDir) + "::" + Access; + std::string Method; + if (_config->Exists(confItem)) + Method = _config->FindFile(confItem.c_str()); + else + Method = _config->FindDir(methodsDir) + Access; if (FileExists(Method) == false) { + if (flNotDir(Method) == "false") + { + _error->Error(_("The method '%s' is explicitly disabled via configuration."), Access.c_str()); + if (Access == "http" || Access == "https") + _error->Notice(_("If you meant to use Tor remember to use %s instead of %s."), ("tor+" + Access).c_str(), Access.c_str()); + return false; + } _error->Error(_("The method driver %s could not be found."),Method.c_str()); - if (Access == "https") - _error->Notice(_("Is the package %s installed?"), "apt-transport-https"); + std::string const A(Access.cbegin(), std::find(Access.cbegin(), Access.cend(), '+')); + std::string pkg; + strprintf(pkg, "apt-transport-%s", A.c_str()); + _error->Notice(_("Is the package %s installed?"), pkg.c_str()); return false; } + std::string const Calling = _config->FindDir(methodsDir) + Access; if (Debug == true) - clog << "Starting method '" << Method << '\'' << endl; + { + std::clog << "Starting method '" << Calling << "'"; + if (Calling != Method) + std::clog << " ( via " << Method << " )"; + std::clog << endl; + } // Create the pipes int Pipes[4] = {-1,-1,-1,-1}; @@ -148,11 +149,9 @@ bool pkgAcquire::Worker::Start() SetCloseExec(STDIN_FILENO,false); SetCloseExec(STDERR_FILENO,false); - const char *Args[2]; - Args[0] = Method.c_str(); - Args[1] = 0; - execv(Args[0],(char **)Args); - cerr << "Failed to exec method " << Args[0] << endl; + const char * const Args[] = { Calling.c_str(), nullptr }; + execv(Method.c_str() ,const_cast(Args)); + std::cerr << "Failed to exec method " << Calling << " ( via " << Method << ")" << endl; _exit(100); } @@ -192,6 +191,25 @@ bool pkgAcquire::Worker::ReadMessages() // --------------------------------------------------------------------- /* This takes the messages from the message queue and runs them through the parsers in order. */ +enum class APT_HIDDEN MessageType { + CAPABILITIES = 100, + LOG = 101, + STATUS = 102, + REDIRECT = 103, + WARNING = 104, + URI_START = 200, + URI_DONE = 201, + URI_FAILURE = 400, + GENERAL_FAILURE = 401, + MEDIA_CHANGE = 403 +}; +static bool isDoomedItem(pkgAcquire::Item const * const Itm) +{ + auto const TransItm = dynamic_cast(Itm); + if (TransItm == nullptr) + return false; + return TransItm->TransactionManager->State != pkgAcqTransactionItem::TransactionStarted; +} bool pkgAcquire::Worker::RunMessages() { while (MessageQueue.empty() == false) @@ -204,88 +222,116 @@ bool pkgAcquire::Worker::RunMessages() // Fetch the message number char *End; - int Number = strtol(Message.c_str(),&End,10); + MessageType const Number = static_cast(strtoul(Message.c_str(),&End,10)); if (End == Message.c_str()) return _error->Error("Invalid message from method %s: %s",Access.c_str(),Message.c_str()); string URI = LookupTag(Message,"URI"); - pkgAcquire::Queue::QItem *Itm = 0; + pkgAcquire::Queue::QItem *Itm = NULL; if (URI.empty() == false) Itm = OwnerQ->FindItem(URI,this); - // update used mirror - string UsedMirror = LookupTag(Message,"UsedMirror", ""); - if (!UsedMirror.empty() && - Itm && - Itm->Description.find(" ") != string::npos) + if (Itm != NULL) { - Itm->Description.replace(0, Itm->Description.find(" "), UsedMirror); - // FIXME: will we need this as well? - //Itm->ShortDesc = UsedMirror; + // update used mirror + string UsedMirror = LookupTag(Message,"UsedMirror", ""); + if (UsedMirror.empty() == false) + { + for (pkgAcquire::Queue::QItem::owner_iterator O = Itm->Owners.begin(); O != Itm->Owners.end(); ++O) + (*O)->UsedMirror = UsedMirror; + + if (Itm->Description.find(" ") != string::npos) + Itm->Description.replace(0, Itm->Description.find(" "), UsedMirror); + } } // Determine the message number and dispatch switch (Number) { - // 100 Capabilities - case 100: + case MessageType::CAPABILITIES: if (Capabilities(Message) == false) return _error->Error("Unable to process Capabilities message from %s",Access.c_str()); break; - // 101 Log - case 101: + case MessageType::LOG: if (Debug == true) clog << " <- (log) " << LookupTag(Message,"Message") << endl; break; - // 102 Status - case 102: + case MessageType::STATUS: Status = LookupTag(Message,"Message"); break; - // 103 Redirect - case 103: + case MessageType::REDIRECT: { - if (Itm == 0) + if (Itm == nullptr) { _error->Error("Method gave invalid 103 Redirect message"); break; } - string NewURI = LookupTag(Message,"New-URI",URI.c_str()); + std::string const NewURI = LookupTag(Message,"New-URI",URI.c_str()); Itm->URI = NewURI; ItemDone(); // Change the status so that it can be dequeued - for (pkgAcquire::Queue::QItem::owner_iterator O = Itm->Owners.begin(); O != Itm->Owners.end(); ++O) - { - pkgAcquire::Item *Owner = *O; - Owner->Status = pkgAcquire::Item::StatIdle; - } + for (auto const &O: Itm->Owners) + O->Status = pkgAcquire::Item::StatIdle; // Mark the item as done (taking care of all queues) // and then put it in the main queue again std::vector const ItmOwners = Itm->Owners; OwnerQ->ItemDone(Itm); - Itm = NULL; - for (pkgAcquire::Queue::QItem::owner_iterator O = ItmOwners.begin(); O != ItmOwners.end(); ++O) + Itm = nullptr; + for (auto const &Owner: ItmOwners) { - pkgAcquire::Item *Owner = *O; - pkgAcquire::ItemDesc desc = Owner->GetItemDesc(); - desc.URI = NewURI; - OwnerQ->Owner->Enqueue(desc); + pkgAcquire::ItemDesc &desc = Owner->GetItemDesc(); + if (Owner->IsRedirectionLoop(NewURI)) + { + std::string msg = Message; + msg.append("\nFailReason: RedirectionLoop"); + Owner->Failed(msg, Config); + if (Log != nullptr) + Log->Fail(Owner->GetItemDesc()); + continue; + } + + if (Log != nullptr) + Log->Done(desc); - if (Log != 0) - Log->Done(Owner->GetItemDesc()); + // if we change site, treat it as a mirror change + if (URI::SiteOnly(NewURI) != URI::SiteOnly(desc.URI)) + { + auto const firstSpace = desc.Description.find(" "); + if (firstSpace != std::string::npos) + { + std::string const OldSite = desc.Description.substr(0, firstSpace); + if (likely(APT::String::Startswith(desc.URI, OldSite))) + { + std::string const OldExtra = desc.URI.substr(OldSite.length() + 1); + if (likely(APT::String::Endswith(NewURI, OldExtra))) + { + std::string const NewSite = NewURI.substr(0, NewURI.length() - OldExtra.length()); + Owner->UsedMirror = URI::ArchiveOnly(NewSite); + desc.Description.replace(0, firstSpace, Owner->UsedMirror); + } + } + } + } + desc.URI = NewURI; + if (isDoomedItem(Owner) == false) + OwnerQ->Owner->Enqueue(desc); } break; } - // 200 URI Start - case 200: + case MessageType::WARNING: + _error->Warning("%s: %s", Itm->Owner->DescURI().c_str(), LookupTag(Message,"Message").c_str()); + break; + + case MessageType::URI_START: { - if (Itm == 0) + if (Itm == nullptr) { _error->Error("Method gave invalid 200 URI Start message"); break; @@ -295,26 +341,24 @@ bool pkgAcquire::Worker::RunMessages() CurrentSize = 0; TotalSize = strtoull(LookupTag(Message,"Size","0").c_str(), NULL, 10); ResumePoint = strtoull(LookupTag(Message,"Resume-Point","0").c_str(), NULL, 10); - for (pkgAcquire::Queue::QItem::owner_iterator O = Itm->Owners.begin(); O != Itm->Owners.end(); ++O) + for (auto const Owner: Itm->Owners) { - (*O)->Start(Message, TotalSize); - + Owner->Start(Message, TotalSize); // Display update before completion - if (Log != 0) + if (Log != nullptr) { if (Log->MorePulses == true) - Log->Pulse((*O)->GetOwner()); - Log->Fetch((*O)->GetItemDesc()); + Log->Pulse(Owner->GetOwner()); + Log->Fetch(Owner->GetItemDesc()); } } break; } - // 201 URI Done - case 201: + case MessageType::URI_DONE: { - if (Itm == 0) + if (Itm == nullptr) { _error->Error("Method gave invalid 201 URI Done message"); break; @@ -327,9 +371,10 @@ bool pkgAcquire::Worker::RunMessages() for (pkgAcquire::Queue::QItem::owner_iterator O = Itm->Owners.begin(); O != Itm->Owners.end(); ++O) Log->Pulse((*O)->GetOwner()); - std::string const filename = LookupTag(Message, "Filename", Itm->Owner->DestFile.c_str()); HashStringList ReceivedHashes; { + std::string const givenfilename = LookupTag(Message, "Filename"); + std::string const filename = givenfilename.empty() ? Itm->Owner->DestFile : givenfilename; // see if we got hashes to verify for (char const * const * type = HashString::SupportedHashes(); *type != NULL; ++type) { @@ -350,11 +395,11 @@ bool pkgAcquire::Worker::RunMessages() ReceivedHashes = calc.GetHashStringList(); } } - } - // only local files can refer other filenames and counting them as fetched would be unfair - if (Log != NULL && filename != Itm->Owner->DestFile) - Log->Fetched(ReceivedHashes.FileSize(),atoi(LookupTag(Message,"Resume-Point","0").c_str())); + // only local files can refer other filenames and counting them as fetched would be unfair + if (Log != NULL && Itm->Owner->Complete == false && Itm->Owner->Local == false && givenfilename == filename) + Log->Fetched(ReceivedHashes.FileSize(),atoi(LookupTag(Message,"Resume-Point","0").c_str())); + } std::vector const ItmOwners = Itm->Owners; OwnerQ->ItemDone(Itm); @@ -362,9 +407,9 @@ bool pkgAcquire::Worker::RunMessages() bool const isIMSHit = StringToBool(LookupTag(Message,"IMS-Hit"),false) || StringToBool(LookupTag(Message,"Alt-IMS-Hit"),false); - for (pkgAcquire::Queue::QItem::owner_iterator O = ItmOwners.begin(); O != ItmOwners.end(); ++O) + auto const forcedHash = _config->Find("Acquire::ForceHash"); + for (auto const Owner: ItmOwners) { - pkgAcquire::Item * const Owner = *O; HashStringList const ExpectedHashes = Owner->GetExpectedHashes(); if(_config->FindB("Debug::pkgAcquire::Auth", false) == true) { @@ -380,9 +425,10 @@ bool pkgAcquire::Worker::RunMessages() // decide if what we got is what we expected bool consideredOkay = false; - if (ExpectedHashes.usable()) + if ((forcedHash.empty() && ExpectedHashes.empty() == false) || + (forcedHash.empty() == false && ExpectedHashes.usable())) { - if (ReceivedHashes.usable() == false) + if (ReceivedHashes.empty()) { /* IMS-Hits can't be checked here as we will have uncompressed file, but the hashes for the compressed file. What we have was good through @@ -395,17 +441,20 @@ bool pkgAcquire::Worker::RunMessages() consideredOkay = false; } - else if (Owner->HashesRequired() == true) - consideredOkay = false; else - consideredOkay = true; + consideredOkay = !Owner->HashesRequired(); if (consideredOkay == true) - { - Owner->Done(Message, ReceivedHashes, Config); + consideredOkay = Owner->VerifyDone(Message, Config); + else // hashsum mismatch + Owner->Status = pkgAcquire::Item::StatAuthError; + - // Log that we are done - if (Log != 0) + if (consideredOkay == true) + { + if (isDoomedItem(Owner) == false) + Owner->Done(Message, ReceivedHashes, Config); + if (Log != nullptr) { if (isIMSHit) Log->IMSHit(Owner->GetItemDesc()); @@ -415,21 +464,29 @@ bool pkgAcquire::Worker::RunMessages() } else { - Owner->Status = pkgAcquire::Item::StatAuthError; - Owner->Failed(Message,Config); - - if (Log != 0) - Log->Fail(Owner->GetItemDesc()); + auto SavedDesc = Owner->GetItemDesc(); + if (isDoomedItem(Owner) == false) + { + if (Message.find("\nFailReason:") == std::string::npos) + { + if (ReceivedHashes != ExpectedHashes) + Message.append("\nFailReason: HashSumMismatch"); + else + Message.append("\nFailReason: WeakHashSums"); + } + Owner->Failed(Message,Config); + } + if (Log != nullptr) + Log->Fail(SavedDesc); } } ItemDone(); break; } - // 400 URI Failure - case 400: + case MessageType::URI_FAILURE: { - if (Itm == 0) + if (Itm == nullptr) { std::string const msg = LookupTag(Message,"Message"); _error->Error("Method gave invalid 400 URI Failure message: %s", msg.c_str()); @@ -439,40 +496,51 @@ bool pkgAcquire::Worker::RunMessages() PrepareFiles("400::URIFailure", Itm); // Display update before completion - if (Log != 0 && Log->MorePulses == true) + if (Log != nullptr && Log->MorePulses == true) for (pkgAcquire::Queue::QItem::owner_iterator O = Itm->Owners.begin(); O != Itm->Owners.end(); ++O) Log->Pulse((*O)->GetOwner()); std::vector const ItmOwners = Itm->Owners; OwnerQ->ItemDone(Itm); - Itm = NULL; + Itm = nullptr; - for (pkgAcquire::Queue::QItem::owner_iterator O = ItmOwners.begin(); O != ItmOwners.end(); ++O) + bool errTransient = false, errAuthErr = false; { - // set some status - if(LookupTag(Message,"FailReason") == "Timeout" || - LookupTag(Message,"FailReason") == "TmpResolveFailure" || - LookupTag(Message,"FailReason") == "ResolveFailure" || - LookupTag(Message,"FailReason") == "ConnectionRefused") - (*O)->Status = pkgAcquire::Item::StatTransientNetworkError; - - (*O)->Failed(Message,Config); + std::string const failReason = LookupTag(Message, "FailReason"); + { + auto const reasons = { "Timeout", "ConnectionRefused", + "ConnectionTimedOut", "ResolveFailure", "TmpResolveFailure" }; + errTransient = std::find(std::begin(reasons), std::end(reasons), failReason) != std::end(reasons); + } + if (errTransient == false) + { + auto const reasons = { "HashSumMismatch", "WeakHashSums", "MaximumSizeExceeded" }; + errAuthErr = std::find(std::begin(reasons), std::end(reasons), failReason) != std::end(reasons); + } + } - if (Log != 0) - Log->Fail((*O)->GetItemDesc()); + for (auto const Owner: ItmOwners) + { + if (errAuthErr && Owner->GetExpectedHashes().empty() == false) + Owner->Status = pkgAcquire::Item::StatAuthError; + else if (errTransient) + Owner->Status = pkgAcquire::Item::StatTransientNetworkError; + auto SavedDesc = Owner->GetItemDesc(); + if (isDoomedItem(Owner) == false) + Owner->Failed(Message,Config); + if (Log != nullptr) + Log->Fail(SavedDesc); } ItemDone(); break; } - // 401 General Failure - case 401: + case MessageType::GENERAL_FAILURE: _error->Error("Method %s General failure: %s",Access.c_str(),LookupTag(Message,"Message").c_str()); break; - // 403 Media Change - case 403: + case MessageType::MEDIA_CHANGE: MediaChange(Message); break; } @@ -526,7 +594,7 @@ bool pkgAcquire::Worker::MediaChange(string Message) ostringstream msg,status; ioprintf(msg,_("Please insert the disc labeled: " "'%s' " - "in the drive '%s' and press enter."), + "in the drive '%s' and press [Enter]."), Media.c_str(),Drive.c_str()); status << "media-change: " // message << Media << ":" // media @@ -593,12 +661,55 @@ bool pkgAcquire::Worker::QueueItem(pkgAcquire::Queue::QItem *Item) if (OutFd == -1) return false; + HashStringList const hsl = Item->GetExpectedHashes(); + + if (isDoomedItem(Item->Owner)) + return true; + + if (hsl.usable() == false && Item->Owner->HashesRequired() && + _config->Exists("Acquire::ForceHash") == false) + { + std::string const Message = "400 URI Failure" + "\nURI: " + Item->URI + + "\nFilename: " + Item->Owner->DestFile + + "\nFailReason: WeakHashSums"; + + auto const ItmOwners = Item->Owners; + for (auto &O: ItmOwners) + { + O->Status = pkgAcquire::Item::StatAuthError; + O->Failed(Message, Config); + if (Log != nullptr) + Log->Fail(O->GetItemDesc()); + } + // "queued" successfully, the item just instantly failed + return true; + } + + if (Item->Owner->IsRedirectionLoop(Item->URI)) + { + std::string const Message = "400 URI Failure" + "\nURI: " + Item->URI + + "\nFilename: " + Item->Owner->DestFile + + "\nFailReason: RedirectionLoop"; + + auto const ItmOwners = Item->Owners; + for (auto &O: ItmOwners) + { + O->Status = pkgAcquire::Item::StatError; + O->Failed(Message, Config); + if (Log != nullptr) + Log->Fail(O->GetItemDesc()); + } + // "queued" successfully, the item just instantly failed + return true; + } + string Message = "600 URI Acquire\n"; Message.reserve(300); Message += "URI: " + Item->URI; Message += "\nFilename: " + Item->Owner->DestFile; - HashStringList const hsl = Item->GetExpectedHashes(); for (HashStringList::const_iterator hs = hsl.begin(); hs != hsl.end(); ++hs) Message += "\nExpected-" + hs->HashType() + ": " + hs->HashValue(); @@ -619,7 +730,7 @@ bool pkgAcquire::Worker::QueueItem(pkgAcquire::Queue::QItem *Item) if (RealFileExists(Item->Owner->DestFile)) { - std::string SandboxUser = _config->Find("APT::Sandbox::User"); + std::string const SandboxUser = _config->Find("APT::Sandbox::User"); ChangeOwnerAndPermissionOfFile("Item::QueueURI", Item->Owner->DestFile.c_str(), SandboxUser.c_str(), "root", 0600); } @@ -722,12 +833,12 @@ void pkgAcquire::Worker::PrepareFiles(char const * const caller, pkgAcquire::Que for (pkgAcquire::Queue::QItem::owner_iterator O = Itm->Owners.begin(); O != Itm->Owners.end(); ++O) { pkgAcquire::Item const * const Owner = *O; - if (Owner->DestFile == filename) + if (Owner->DestFile == filename || filename == "/dev/null") continue; - unlink(Owner->DestFile.c_str()); + RemoveFile("PrepareFiles", Owner->DestFile); if (link(filename.c_str(), Owner->DestFile.c_str()) != 0) { - // diferent mounts can't happen for us as we download to lists/ by default, + // different mounts can't happen for us as we download to lists/ by default, // but if the system is reused by others the locations can potentially be on // different disks, so use symlink as poor-men replacement. // FIXME: Real copying as last fallback, but that is costly, so offload to a method preferable @@ -739,7 +850,7 @@ void pkgAcquire::Worker::PrepareFiles(char const * const caller, pkgAcquire::Que else { for (pkgAcquire::Queue::QItem::owner_iterator O = Itm->Owners.begin(); O != Itm->Owners.end(); ++O) - unlink((*O)->DestFile.c_str()); + RemoveFile("PrepareFiles", (*O)->DestFile); } } /*}}}*/