+ Cycle();
+}
+ /*}}}*/
+HashStringList pkgAcquire::Queue::QItem::GetExpectedHashes() const /*{{{*/
+{
+ /* each Item can have multiple owners and each owner might have different
+ hashes, even if that is unlikely in practice and if so at least some
+ owners will later fail. There is one situation through which is not a
+ failure and still needs this handling: Two owners who expect the same
+ file, but one owner only knows the SHA1 while the other only knows SHA256. */
+ HashStringList superhsl;
+ for (pkgAcquire::Queue::QItem::owner_iterator O = Owners.begin(); O != Owners.end(); ++O)
+ {
+ HashStringList const hsl = (*O)->GetExpectedHashes();
+ if (hsl.usable() == false)
+ continue;
+ if (superhsl.usable() == false)
+ superhsl = hsl;
+ else
+ {
+ // we merge both lists - if we find disagreement send no hashes
+ HashStringList::const_iterator hs = hsl.begin();
+ for (; hs != hsl.end(); ++hs)
+ if (superhsl.push_back(*hs) == false)
+ break;
+ if (hs != hsl.end())
+ {
+ superhsl.clear();
+ break;
+ }
+ }
+ }
+ return superhsl;
+}
+ /*}}}*/
+APT_PURE unsigned long long pkgAcquire::Queue::QItem::GetMaximumSize() const /*{{{*/
+{
+ unsigned long long Maximum = std::numeric_limits<unsigned long long>::max();
+ for (auto const &O: Owners)
+ {
+ if (O->FileSize == 0)
+ continue;
+ Maximum = std::min(Maximum, O->FileSize);
+ }
+ if (Maximum == std::numeric_limits<unsigned long long>::max())
+ return 0;
+ return Maximum;
+}
+ /*}}}*/
+void pkgAcquire::Queue::QItem::SyncDestinationFiles() const /*{{{*/
+{
+ /* ensure that the first owner has the best partial file of all and
+ the rest have (potentially dangling) symlinks to it so that
+ everything (like progress reporting) finds it easily */
+ std::string superfile = Owner->DestFile;
+ off_t supersize = 0;
+ for (pkgAcquire::Queue::QItem::owner_iterator O = Owners.begin(); O != Owners.end(); ++O)
+ {
+ if ((*O)->DestFile == superfile)
+ continue;
+ struct stat file;
+ if (lstat((*O)->DestFile.c_str(),&file) == 0)
+ {
+ if ((file.st_mode & S_IFREG) == 0)
+ RemoveFile("SyncDestinationFiles", (*O)->DestFile);
+ else if (supersize < file.st_size)
+ {
+ supersize = file.st_size;
+ RemoveFile("SyncDestinationFiles", superfile);
+ rename((*O)->DestFile.c_str(), superfile.c_str());
+ }
+ else
+ RemoveFile("SyncDestinationFiles", (*O)->DestFile);
+ if (symlink(superfile.c_str(), (*O)->DestFile.c_str()) != 0)
+ {
+ ; // not a problem per-se and no real alternative
+ }
+ }
+ }
+}
+ /*}}}*/
+std::string pkgAcquire::Queue::QItem::Custom600Headers() const /*{{{*/
+{
+ /* The others are relatively easy to merge, but this one?
+ Lets not merge and see how far we can run with it…
+ Likely, nobody will ever notice as all the items will
+ be of the same class and hence generate the same headers. */
+ return Owner->Custom600Headers();
+}
+ /*}}}*/
+
+// AcquireStatus::pkgAcquireStatus - Constructor /*{{{*/
+// ---------------------------------------------------------------------
+/* */
+pkgAcquireStatus::pkgAcquireStatus() : d(NULL), Percent(-1), Update(true), MorePulses(false)
+{
+ Start();
+}
+ /*}}}*/
+// AcquireStatus::Pulse - Called periodically /*{{{*/
+// ---------------------------------------------------------------------
+/* This computes some internal state variables for the derived classes to
+ use. It generates the current downloaded bytes and total bytes to download
+ as well as the current CPS estimate. */
+bool pkgAcquireStatus::Pulse(pkgAcquire *Owner)
+{
+ TotalBytes = 0;
+ CurrentBytes = 0;
+ TotalItems = 0;
+ CurrentItems = 0;
+
+ // Compute the total number of bytes to fetch
+ unsigned int Unknown = 0;
+ unsigned int Count = 0;
+ bool ExpectAdditionalItems = false;
+ for (pkgAcquire::ItemCIterator I = Owner->ItemsBegin();
+ I != Owner->ItemsEnd();
+ ++I, ++Count)
+ {
+ TotalItems++;
+ if ((*I)->Status == pkgAcquire::Item::StatDone)
+ ++CurrentItems;
+
+ // do we expect to acquire more files than we know of yet?
+ if ((*I)->ExpectedAdditionalItems > 0)
+ ExpectAdditionalItems = true;
+
+ TotalBytes += (*I)->FileSize;
+ if ((*I)->Complete == true)
+ CurrentBytes += (*I)->FileSize;
+ if ((*I)->FileSize == 0 && (*I)->Complete == false)
+ ++Unknown;
+ }
+
+ // Compute the current completion
+ unsigned long long ResumeSize = 0;
+ for (pkgAcquire::Worker *I = Owner->WorkersBegin(); I != 0;
+ I = Owner->WorkerStep(I))
+ {
+ if (I->CurrentItem != 0 && I->CurrentItem->Owner->Complete == false)
+ {
+ CurrentBytes += I->CurrentSize;
+ ResumeSize += I->ResumePoint;
+
+ // Files with unknown size always have 100% completion
+ if (I->CurrentItem->Owner->FileSize == 0 &&
+ I->CurrentItem->Owner->Complete == false)
+ TotalBytes += I->CurrentSize;
+ }
+ }
+
+ // Normalize the figures and account for unknown size downloads
+ if (TotalBytes <= 0)
+ TotalBytes = 1;
+ if (Unknown == Count)
+ TotalBytes = Unknown;
+
+ // Wha?! Is not supposed to happen.
+ if (CurrentBytes > TotalBytes)
+ CurrentBytes = TotalBytes;
+
+ // Compute the CPS
+ struct timeval NewTime;
+ gettimeofday(&NewTime,0);
+ if ((NewTime.tv_sec - Time.tv_sec == 6 && NewTime.tv_usec > Time.tv_usec) ||
+ NewTime.tv_sec - Time.tv_sec > 6)
+ {
+ double Delta = NewTime.tv_sec - Time.tv_sec +
+ (NewTime.tv_usec - Time.tv_usec)/1000000.0;
+
+ // Compute the CPS value
+ if (Delta < 0.01)
+ CurrentCPS = 0;
+ else
+ CurrentCPS = ((CurrentBytes - ResumeSize) - LastBytes)/Delta;
+ LastBytes = CurrentBytes - ResumeSize;
+ ElapsedTime = (unsigned long long)Delta;
+ Time = NewTime;
+ }
+
+ double const OldPercent = Percent;
+ // calculate the percentage, if we have too little data assume 1%
+ if (ExpectAdditionalItems)
+ Percent = 0;
+ else
+ // use both files and bytes because bytes can be unreliable
+ Percent = (0.8 * (CurrentBytes/float(TotalBytes)*100.0) +
+ 0.2 * (CurrentItems/float(TotalItems)*100.0));
+
+ // debug
+ if (_config->FindB("Debug::acquire::progress", false) == true)
+ {
+ std::clog
+ << "["
+ << std::setw(5) << std::setprecision(4) << std::showpoint << Percent
+ << "]"
+ << " Bytes: "
+ << SizeToStr(CurrentBytes) << " / " << SizeToStr(TotalBytes)
+ << " # Files: "
+ << CurrentItems << " / " << TotalItems
+ << std::endl;
+ }
+
+ double const DiffPercent = Percent - OldPercent;
+ if (DiffPercent < 0.001 && _config->FindB("Acquire::Progress::Diffpercent", false) == true)
+ return true;
+
+ int fd = _config->FindI("APT::Status-Fd",-1);
+ if(fd > 0)
+ {
+ ostringstream status;
+
+ char msg[200];
+ long i = CurrentItems < TotalItems ? CurrentItems + 1 : CurrentItems;
+ unsigned long long ETA = 0;
+ if(CurrentCPS > 0)
+ ETA = (TotalBytes - CurrentBytes) / CurrentCPS;
+
+ // only show the ETA if it makes sense
+ if (ETA > 0 && ETA < 172800 /* two days */ )
+ snprintf(msg,sizeof(msg), _("Retrieving file %li of %li (%s remaining)"), i, TotalItems, TimeToStr(ETA).c_str());
+ else
+ snprintf(msg,sizeof(msg), _("Retrieving file %li of %li"), i, TotalItems);
+
+ // build the status str
+ std::string dlstatus;
+ strprintf(dlstatus, "dlstatus:%ld:%.4f:%s\n", i, Percent, msg);
+ FileFd::Write(fd, dlstatus.data(), dlstatus.size());
+ }
+
+ return true;
+}
+ /*}}}*/
+// AcquireStatus::Start - Called when the download is started /*{{{*/
+// ---------------------------------------------------------------------
+/* We just reset the counters */
+void pkgAcquireStatus::Start()
+{
+ gettimeofday(&Time,0);
+ gettimeofday(&StartTime,0);
+ LastBytes = 0;
+ CurrentCPS = 0;
+ CurrentBytes = 0;
+ TotalBytes = 0;
+ FetchedBytes = 0;
+ ElapsedTime = 0;
+ TotalItems = 0;
+ CurrentItems = 0;
+}
+ /*}}}*/
+// AcquireStatus::Stop - Finished downloading /*{{{*/
+// ---------------------------------------------------------------------
+/* This accurately computes the elapsed time and the total overall CPS. */
+void pkgAcquireStatus::Stop()
+{
+ // Compute the CPS and elapsed time
+ struct timeval NewTime;
+ gettimeofday(&NewTime,0);
+
+ double Delta = NewTime.tv_sec - StartTime.tv_sec +
+ (NewTime.tv_usec - StartTime.tv_usec)/1000000.0;
+
+ // Compute the CPS value
+ if (Delta < 0.01)
+ CurrentCPS = 0;
+ else
+ CurrentCPS = FetchedBytes/Delta;
+ LastBytes = CurrentBytes;
+ ElapsedTime = (unsigned long long)Delta;