]> git.saurik.com Git - apt.git/blame - apt-pkg/acquire.cc
Avoid overflow when summing up file sizes
[apt.git] / apt-pkg / acquire.cc
CommitLineData
0118833a
AL
1// -*- mode: cpp; mode: fold -*-
2// Description /*{{{*/
1b480911 3// $Id: acquire.cc,v 1.50 2004/03/17 05:17:11 mdz Exp $
0118833a
AL
4/* ######################################################################
5
6 Acquire - File Acquiration
7
1e3f4083 8 The core element for the schedule system is the concept of a named
0a8a80e5 9 queue. Each queue is unique and each queue has a name derived from the
1e3f4083 10 URI. The degree of paralization can be controlled by how the queue
0a8a80e5
AL
11 name is derived from the URI.
12
0118833a
AL
13 ##################################################################### */
14 /*}}}*/
15// Include Files /*{{{*/
ea542140
DK
16#include <config.h>
17
0118833a
AL
18#include <apt-pkg/acquire.h>
19#include <apt-pkg/acquire-item.h>
20#include <apt-pkg/acquire-worker.h>
0a8a80e5
AL
21#include <apt-pkg/configuration.h>
22#include <apt-pkg/error.h>
cdcc6d34 23#include <apt-pkg/strutl.h>
1cd1c398 24#include <apt-pkg/fileutl.h>
8267fe24 25
08ea7806 26#include <algorithm>
ae732225 27#include <numeric>
453b82a3
DK
28#include <string>
29#include <vector>
b4fc9b6f 30#include <iostream>
ac7f8f79
MV
31#include <sstream>
32#include <iomanip>
514a25cb 33#include <memory>
04a54261 34
526334a0 35#include <stdio.h>
453b82a3
DK
36#include <stdlib.h>
37#include <string.h>
38#include <unistd.h>
7c8206bf 39#include <fcntl.h>
04a54261
DK
40#include <pwd.h>
41#include <grp.h>
7a7fa5f0 42#include <dirent.h>
8267fe24 43#include <sys/time.h>
453b82a3 44#include <sys/select.h>
524f8105 45#include <errno.h>
56472095 46#include <sys/stat.h>
ea542140
DK
47
48#include <apti18n.h>
0118833a
AL
49 /*}}}*/
50
b4fc9b6f
AL
51using namespace std;
52
0118833a
AL
53// Acquire::pkgAcquire - Constructor /*{{{*/
54// ---------------------------------------------------------------------
93bf083d 55/* We grab some runtime state from the configuration space */
6c55f07a 56pkgAcquire::pkgAcquire() : LockFD(-1), d(NULL), Queues(0), Workers(0), Configs(0), Log(NULL), ToFetch(0),
1cd1c398 57 Debug(_config->FindB("Debug::pkgAcquire",false)),
5efbd596 58 Running(false)
0118833a 59{
03aa0847 60 Initialize();
1cd1c398 61}
6c55f07a 62pkgAcquire::pkgAcquire(pkgAcquireStatus *Progress) : LockFD(-1), d(NULL), Queues(0), Workers(0),
04a54261 63 Configs(0), Log(NULL), ToFetch(0),
1cd1c398 64 Debug(_config->FindB("Debug::pkgAcquire",false)),
5efbd596 65 Running(false)
03aa0847
DK
66{
67 Initialize();
68 SetLog(Progress);
69}
70void pkgAcquire::Initialize()
1cd1c398
DK
71{
72 string const Mode = _config->Find("Acquire::Queue-Mode","host");
73 if (strcasecmp(Mode.c_str(),"host") == 0)
74 QueueMode = QueueHost;
75 if (strcasecmp(Mode.c_str(),"access") == 0)
76 QueueMode = QueueAccess;
03aa0847
DK
77
78 // chown the auth.conf file as it will be accessed by our methods
79 std::string const SandboxUser = _config->Find("APT::Sandbox::User");
514a25cb 80 if (getuid() == 0 && SandboxUser.empty() == false && SandboxUser != "root") // if we aren't root, we can't chown, so don't try it
03aa0847
DK
81 {
82 struct passwd const * const pw = getpwnam(SandboxUser.c_str());
83 struct group const * const gr = getgrnam("root");
84 if (pw != NULL && gr != NULL)
85 {
86 std::string const AuthConf = _config->FindFile("Dir::Etc::netrc");
87 if(AuthConf.empty() == false && RealFileExists(AuthConf) &&
88 chown(AuthConf.c_str(), pw->pw_uid, gr->gr_gid) != 0)
89 _error->WarningE("SetupAPTPartialDirectory", "chown to %s:root of file %s failed", SandboxUser.c_str(), AuthConf.c_str());
90 }
91 }
1cd1c398
DK
92}
93 /*}}}*/
04a54261
DK
94// Acquire::GetLock - lock directory and prepare for action /*{{{*/
95static bool SetupAPTPartialDirectory(std::string const &grand, std::string const &parent)
1cd1c398 96{
04a54261 97 std::string const partial = parent + "partial";
8fe964f1
DK
98 mode_t const mode = umask(S_IWGRP | S_IWOTH);
99 bool const creation_fail = (CreateAPTDirectoryIfNeeded(grand, partial) == false &&
100 CreateAPTDirectoryIfNeeded(parent, partial) == false);
101 umask(mode);
102 if (creation_fail == true)
04a54261 103 return false;
0a8a80e5 104
03aa0847 105 std::string const SandboxUser = _config->Find("APT::Sandbox::User");
514a25cb 106 if (getuid() == 0 && SandboxUser.empty() == false && SandboxUser != "root") // if we aren't root, we can't chown, so don't try it
04a54261 107 {
03aa0847
DK
108 struct passwd const * const pw = getpwnam(SandboxUser.c_str());
109 struct group const * const gr = getgrnam("root");
1924b1e5
MV
110 if (pw != NULL && gr != NULL)
111 {
112 // chown the partial dir
113 if(chown(partial.c_str(), pw->pw_uid, gr->gr_gid) != 0)
114 _error->WarningE("SetupAPTPartialDirectory", "chown to %s:root of directory %s failed", SandboxUser.c_str(), partial.c_str());
1924b1e5 115 }
04a54261
DK
116 }
117 if (chmod(partial.c_str(), 0700) != 0)
118 _error->WarningE("SetupAPTPartialDirectory", "chmod 0700 of directory %s failed", partial.c_str());
119
120 return true;
121}
122bool pkgAcquire::Setup(pkgAcquireStatus *Progress, string const &Lock)
123{
124 Log = Progress;
125 if (Lock.empty())
43acd019
DK
126 {
127 string const listDir = _config->FindDir("Dir::State::lists");
04a54261
DK
128 if (SetupAPTPartialDirectory(_config->FindDir("Dir::State"), listDir) == false)
129 return _error->Errno("Acquire", _("List directory %spartial is missing."), listDir.c_str());
43acd019 130 string const archivesDir = _config->FindDir("Dir::Cache::Archives");
04a54261
DK
131 if (SetupAPTPartialDirectory(_config->FindDir("Dir::Cache"), archivesDir) == false)
132 return _error->Errno("Acquire", _("Archives directory %spartial is missing."), archivesDir.c_str());
133 return true;
134 }
135 return GetLock(Lock);
136}
137bool pkgAcquire::GetLock(std::string const &Lock)
138{
139 if (Lock.empty() == true)
140 return false;
9c2c9c24 141
04a54261
DK
142 // check for existence and possibly create auxiliary directories
143 string const listDir = _config->FindDir("Dir::State::lists");
144 string const archivesDir = _config->FindDir("Dir::Cache::Archives");
9c2c9c24 145
04a54261
DK
146 if (Lock == listDir)
147 {
148 if (SetupAPTPartialDirectory(_config->FindDir("Dir::State"), listDir) == false)
149 return _error->Errno("Acquire", _("List directory %spartial is missing."), listDir.c_str());
150 }
151 if (Lock == archivesDir)
152 {
153 if (SetupAPTPartialDirectory(_config->FindDir("Dir::Cache"), archivesDir) == false)
43acd019
DK
154 return _error->Errno("Acquire", _("Archives directory %spartial is missing."), archivesDir.c_str());
155 }
1cd1c398 156
04a54261 157 if (_config->FindB("Debug::NoLocking", false) == true)
1cd1c398
DK
158 return true;
159
160 // Lock the directory this acquire object will work in
7e04a6bf
DK
161 if (LockFD != -1)
162 close(LockFD);
04a54261 163 LockFD = ::GetLock(flCombine(Lock, "lock"));
1cd1c398
DK
164 if (LockFD == -1)
165 return _error->Error(_("Unable to lock directory %s"), Lock.c_str());
166
167 return true;
168}
169 /*}}}*/
0118833a
AL
170// Acquire::~pkgAcquire - Destructor /*{{{*/
171// ---------------------------------------------------------------------
93bf083d 172/* Free our memory, clean up the queues (destroy the workers) */
0118833a
AL
173pkgAcquire::~pkgAcquire()
174{
459681d3 175 Shutdown();
1cd1c398
DK
176
177 if (LockFD != -1)
178 close(LockFD);
179
3b5421b4
AL
180 while (Configs != 0)
181 {
182 MethodConfig *Jnk = Configs;
183 Configs = Configs->Next;
184 delete Jnk;
185 }
281daf46
AL
186}
187 /*}}}*/
8e5fc8f5 188// Acquire::Shutdown - Clean out the acquire object /*{{{*/
281daf46
AL
189// ---------------------------------------------------------------------
190/* */
191void pkgAcquire::Shutdown()
192{
f7f0d6c7 193 while (Items.empty() == false)
1b480911
AL
194 {
195 if (Items[0]->Status == Item::StatFetching)
196 Items[0]->Status = Item::StatError;
281daf46 197 delete Items[0];
1b480911 198 }
0a8a80e5
AL
199
200 while (Queues != 0)
201 {
202 Queue *Jnk = Queues;
203 Queues = Queues->Next;
204 delete Jnk;
205 }
0118833a
AL
206}
207 /*}}}*/
208// Acquire::Add - Add a new item /*{{{*/
209// ---------------------------------------------------------------------
93bf083d
AL
210/* This puts an item on the acquire list. This list is mainly for tracking
211 item status */
0118833a
AL
212void pkgAcquire::Add(Item *Itm)
213{
214 Items.push_back(Itm);
215}
216 /*}}}*/
217// Acquire::Remove - Remove a item /*{{{*/
218// ---------------------------------------------------------------------
93bf083d 219/* Remove an item from the acquire list. This is usually not used.. */
0118833a
AL
220void pkgAcquire::Remove(Item *Itm)
221{
a3eaf954
AL
222 Dequeue(Itm);
223
753b3525 224 for (ItemIterator I = Items.begin(); I != Items.end();)
0118833a
AL
225 {
226 if (*I == Itm)
b4fc9b6f 227 {
0118833a 228 Items.erase(I);
b4fc9b6f
AL
229 I = Items.begin();
230 }
753b3525 231 else
f7f0d6c7 232 ++I;
8267fe24 233 }
0118833a
AL
234}
235 /*}}}*/
0a8a80e5
AL
236// Acquire::Add - Add a worker /*{{{*/
237// ---------------------------------------------------------------------
93bf083d
AL
238/* A list of workers is kept so that the select loop can direct their FD
239 usage. */
0a8a80e5
AL
240void pkgAcquire::Add(Worker *Work)
241{
242 Work->NextAcquire = Workers;
243 Workers = Work;
244}
245 /*}}}*/
246// Acquire::Remove - Remove a worker /*{{{*/
247// ---------------------------------------------------------------------
93bf083d
AL
248/* A worker has died. This can not be done while the select loop is running
249 as it would require that RunFds could handling a changing list state and
1e3f4083 250 it can't.. */
0a8a80e5
AL
251void pkgAcquire::Remove(Worker *Work)
252{
93bf083d
AL
253 if (Running == true)
254 abort();
255
0a8a80e5
AL
256 Worker **I = &Workers;
257 for (; *I != 0;)
258 {
259 if (*I == Work)
260 *I = (*I)->NextAcquire;
261 else
262 I = &(*I)->NextAcquire;
263 }
264}
265 /*}}}*/
0118833a
AL
266// Acquire::Enqueue - Queue an URI for fetching /*{{{*/
267// ---------------------------------------------------------------------
93bf083d 268/* This is the entry point for an item. An item calls this function when
281daf46 269 it is constructed which creates a queue (based on the current queue
93bf083d
AL
270 mode) and puts the item in that queue. If the system is running then
271 the queue might be started. */
8267fe24 272void pkgAcquire::Enqueue(ItemDesc &Item)
0118833a 273{
0a8a80e5 274 // Determine which queue to put the item in
e331f6ed
AL
275 const MethodConfig *Config;
276 string Name = QueueName(Item.URI,Config);
0a8a80e5
AL
277 if (Name.empty() == true)
278 return;
279
280 // Find the queue structure
281 Queue *I = Queues;
282 for (; I != 0 && I->Name != Name; I = I->Next);
283 if (I == 0)
284 {
285 I = new Queue(Name,this);
286 I->Next = Queues;
287 Queues = I;
93bf083d
AL
288
289 if (Running == true)
290 I->Startup();
0a8a80e5 291 }
bfd22fc0 292
e331f6ed
AL
293 // See if this is a local only URI
294 if (Config->LocalOnly == true && Item.Owner->Complete == false)
295 Item.Owner->Local = true;
8267fe24 296 Item.Owner->Status = Item::StatIdle;
0a8a80e5
AL
297
298 // Queue it into the named queue
c03462c6
MV
299 if(I->Enqueue(Item))
300 ToFetch++;
301
0a8a80e5
AL
302 // Some trace stuff
303 if (Debug == true)
304 {
8267fe24
AL
305 clog << "Fetching " << Item.URI << endl;
306 clog << " to " << Item.Owner->DestFile << endl;
e331f6ed 307 clog << " Queue is: " << Name << endl;
0a8a80e5 308 }
3b5421b4
AL
309}
310 /*}}}*/
0a8a80e5 311// Acquire::Dequeue - Remove an item from all queues /*{{{*/
3b5421b4 312// ---------------------------------------------------------------------
93bf083d
AL
313/* This is called when an item is finished being fetched. It removes it
314 from all the queues */
0a8a80e5
AL
315void pkgAcquire::Dequeue(Item *Itm)
316{
317 Queue *I = Queues;
bfd22fc0 318 bool Res = false;
93bf083d
AL
319 if (Debug == true)
320 clog << "Dequeuing " << Itm->DestFile << endl;
5674f6b3
RG
321
322 for (; I != 0; I = I->Next)
323 {
324 if (I->Dequeue(Itm))
325 {
326 Res = true;
327 if (Debug == true)
328 clog << "Dequeued from " << I->Name << endl;
329 }
330 }
331
bfd22fc0
AL
332 if (Res == true)
333 ToFetch--;
0a8a80e5
AL
334}
335 /*}}}*/
336// Acquire::QueueName - Return the name of the queue for this URI /*{{{*/
337// ---------------------------------------------------------------------
338/* The string returned depends on the configuration settings and the
339 method parameters. Given something like http://foo.org/bar it can
340 return http://foo.org or http */
e331f6ed 341string pkgAcquire::QueueName(string Uri,MethodConfig const *&Config)
3b5421b4 342{
93bf083d
AL
343 URI U(Uri);
344
e331f6ed 345 Config = GetConfig(U.Access);
0a8a80e5
AL
346 if (Config == 0)
347 return string();
348
349 /* Single-Instance methods get exactly one queue per URI. This is
350 also used for the Access queue method */
351 if (Config->SingleInstance == true || QueueMode == QueueAccess)
5674f6b3
RG
352 return U.Access;
353
354 string AccessSchema = U.Access + ':',
355 FullQueueName = AccessSchema + U.Host;
356 unsigned int Instances = 0, SchemaLength = AccessSchema.length();
357
358 Queue *I = Queues;
359 for (; I != 0; I = I->Next) {
360 // if the queue already exists, re-use it
361 if (I->Name == FullQueueName)
362 return FullQueueName;
363
364 if (I->Name.compare(0, SchemaLength, AccessSchema) == 0)
365 Instances++;
366 }
367
368 if (Debug) {
369 clog << "Found " << Instances << " instances of " << U.Access << endl;
370 }
371
372 if (Instances >= (unsigned int)_config->FindI("Acquire::QueueHost::Limit",10))
373 return U.Access;
93bf083d 374
5674f6b3 375 return FullQueueName;
0118833a
AL
376}
377 /*}}}*/
3b5421b4
AL
378// Acquire::GetConfig - Fetch the configuration information /*{{{*/
379// ---------------------------------------------------------------------
380/* This locates the configuration structure for an access method. If
381 a config structure cannot be found a Worker will be created to
382 retrieve it */
0a8a80e5 383pkgAcquire::MethodConfig *pkgAcquire::GetConfig(string Access)
3b5421b4
AL
384{
385 // Search for an existing config
386 MethodConfig *Conf;
387 for (Conf = Configs; Conf != 0; Conf = Conf->Next)
388 if (Conf->Access == Access)
389 return Conf;
390
391 // Create the new config class
392 Conf = new MethodConfig;
393 Conf->Access = Access;
394 Conf->Next = Configs;
395 Configs = Conf;
0118833a 396
3b5421b4
AL
397 // Create the worker to fetch the configuration
398 Worker Work(Conf);
399 if (Work.Start() == false)
400 return 0;
7c6e2dc7
MV
401
402 /* if a method uses DownloadLimit, we switch to SingleInstance mode */
4b65cc13 403 if(_config->FindI("Acquire::"+Access+"::Dl-Limit",0) > 0)
7c6e2dc7
MV
404 Conf->SingleInstance = true;
405
3b5421b4
AL
406 return Conf;
407}
408 /*}}}*/
0a8a80e5
AL
409// Acquire::SetFds - Deal with readable FDs /*{{{*/
410// ---------------------------------------------------------------------
411/* Collect FDs that have activity monitors into the fd sets */
412void pkgAcquire::SetFds(int &Fd,fd_set *RSet,fd_set *WSet)
413{
414 for (Worker *I = Workers; I != 0; I = I->NextAcquire)
415 {
416 if (I->InReady == true && I->InFd >= 0)
417 {
418 if (Fd < I->InFd)
419 Fd = I->InFd;
420 FD_SET(I->InFd,RSet);
421 }
422 if (I->OutReady == true && I->OutFd >= 0)
423 {
424 if (Fd < I->OutFd)
425 Fd = I->OutFd;
426 FD_SET(I->OutFd,WSet);
427 }
428 }
429}
430 /*}}}*/
a416a90b
MV
431// Acquire::RunFds - compatibility remove on next abi/api break /*{{{*/
432void pkgAcquire::RunFds(fd_set *RSet,fd_set *WSet)
433{
434 RunFdsSane(RSet, WSet);
435};
436 /*}}}*/
437// Acquire::RunFdsSane - Deal with active FDs /*{{{*/
0a8a80e5 438// ---------------------------------------------------------------------
93bf083d
AL
439/* Dispatch active FDs over to the proper workers. It is very important
440 that a worker never be erased while this is running! The queue class
441 should never erase a worker except during shutdown processing. */
a416a90b 442bool pkgAcquire::RunFdsSane(fd_set *RSet,fd_set *WSet)
0a8a80e5 443{
a416a90b
MV
444 bool Res = true;
445
0a8a80e5
AL
446 for (Worker *I = Workers; I != 0; I = I->NextAcquire)
447 {
448 if (I->InFd >= 0 && FD_ISSET(I->InFd,RSet) != 0)
a416a90b 449 Res &= I->InFdReady();
0a8a80e5 450 if (I->OutFd >= 0 && FD_ISSET(I->OutFd,WSet) != 0)
a416a90b 451 Res &= I->OutFdReady();
0a8a80e5 452 }
a416a90b
MV
453
454 return Res;
0a8a80e5
AL
455}
456 /*}}}*/
457// Acquire::Run - Run the fetch sequence /*{{{*/
458// ---------------------------------------------------------------------
459/* This runs the queues. It manages a select loop for all of the
460 Worker tasks. The workers interact with the queues and items to
461 manage the actual fetch. */
514a25cb
DK
462static bool IsAccessibleBySandboxUser(std::string const &filename, bool const ReadWrite)
463{
464 // you would think this is easily to answer with faccessat, right? Wrong!
465 // It e.g. gets groups wrong, so the only thing which works reliable is trying
466 // to open the file we want to open later on…
467 if (unlikely(filename.empty()))
468 return true;
469
470 if (ReadWrite == false)
471 {
472 errno = 0;
473 // can we read a file? Note that non-existing files are "fine"
474 int const fd = open(filename.c_str(), O_RDONLY | O_CLOEXEC);
475 if (fd == -1 && errno == EACCES)
476 return false;
477 close(fd);
478 return true;
479 }
480 else
481 {
482 // the file might not exist yet and even if it does we will fix permissions,
483 // so important is here just that the directory it is in allows that
484 std::string const dirname = flNotFile(filename);
485 if (unlikely(dirname.empty()))
486 return true;
487
488 char const * const filetag = ".apt-acquire-privs-test.XXXXXX";
489 std::string const tmpfile_tpl = flCombine(dirname, filetag);
490 std::unique_ptr<char, decltype(std::free) *> tmpfile { strdup(tmpfile_tpl.c_str()), std::free };
491 int const fd = mkstemp(tmpfile.get());
492 if (fd == -1 && errno == EACCES)
493 return false;
494 RemoveFile("IsAccessibleBySandboxUser", tmpfile.get());
495 close(fd);
496 return true;
497 }
498}
7c8206bf
DK
499static void CheckDropPrivsMustBeDisabled(pkgAcquire const &Fetcher)
500{
501 if(getuid() != 0)
502 return;
503
514a25cb
DK
504 std::string const SandboxUser = _config->Find("APT::Sandbox::User");
505 if (SandboxUser.empty() || SandboxUser == "root")
7c8206bf
DK
506 return;
507
508 struct passwd const * const pw = getpwnam(SandboxUser.c_str());
509 if (pw == NULL)
ef39c148
MV
510 {
511 _error->Warning(_("No sandbox user '%s' on the system, can not drop privileges"), SandboxUser.c_str());
512 _config->Set("APT::Sandbox::User", "");
7c8206bf 513 return;
ef39c148 514 }
7c8206bf 515
226c0f64
DK
516 gid_t const old_euid = geteuid();
517 gid_t const old_egid = getegid();
514a25cb
DK
518
519 long const ngroups_max = sysconf(_SC_NGROUPS_MAX);
520 std::unique_ptr<gid_t[]> old_gidlist(new gid_t[ngroups_max]);
521 if (unlikely(old_gidlist == NULL))
522 return;
523 ssize_t old_gidlist_nr;
524 if ((old_gidlist_nr = getgroups(ngroups_max, old_gidlist.get())) < 0)
525 {
526 _error->FatalE("getgroups", "getgroups %lu failed", ngroups_max);
527 old_gidlist[0] = 0;
528 old_gidlist_nr = 1;
529 }
530 if (setgroups(1, &pw->pw_gid))
531 _error->FatalE("setgroups", "setgroups %u failed", pw->pw_gid);
532
7c8206bf 533 if (setegid(pw->pw_gid) != 0)
514a25cb 534 _error->FatalE("setegid", "setegid %u failed", pw->pw_gid);
7c8206bf 535 if (seteuid(pw->pw_uid) != 0)
514a25cb 536 _error->FatalE("seteuid", "seteuid %u failed", pw->pw_uid);
7c8206bf 537
7c8206bf 538 for (pkgAcquire::ItemCIterator I = Fetcher.ItemsBegin();
69b76544 539 I != Fetcher.ItemsEnd(); ++I)
7c8206bf 540 {
226c0f64
DK
541 // no need to drop privileges for a complete file
542 if ((*I)->Complete == true)
7c8206bf
DK
543 continue;
544
514a25cb
DK
545 // if destination file is inaccessible all hope is lost for privilege dropping
546 if (IsAccessibleBySandboxUser((*I)->DestFile, true) == false)
7c8206bf 547 {
7c8206bf 548 _error->WarningE("pkgAcquire::Run", _("Can't drop privileges for downloading as file '%s' couldn't be accessed by user '%s'."),
514a25cb 549 (*I)->DestFile.c_str(), SandboxUser.c_str());
7c8206bf
DK
550 _config->Set("APT::Sandbox::User", "");
551 break;
552 }
514a25cb
DK
553
554 // if its the source file (e.g. local sources) we might be lucky
555 // by dropping the dropping only for some methods.
556 URI const source = (*I)->DescURI();
557 if (source.Access == "file" || source.Access == "copy")
558 {
559 std::string const conf = "Binary::" + source.Access + "::APT::Sandbox::User";
560 if (_config->Exists(conf) == true)
561 continue;
562
563 if (IsAccessibleBySandboxUser(source.Path, false) == false)
564 {
565 _error->NoticeE("pkgAcquire::Run", _("Can't drop privileges for downloading as file '%s' couldn't be accessed by user '%s'."),
566 source.Path.c_str(), SandboxUser.c_str());
567 _config->CndSet("Binary::file::APT::Sandbox::User", "root");
568 _config->CndSet("Binary::copy::APT::Sandbox::User", "root");
569 }
570 }
7c8206bf
DK
571 }
572
226c0f64 573 if (seteuid(old_euid) != 0)
514a25cb 574 _error->FatalE("seteuid", "seteuid %u failed", old_euid);
226c0f64 575 if (setegid(old_egid) != 0)
514a25cb
DK
576 _error->FatalE("setegid", "setegid %u failed", old_egid);
577 if (setgroups(old_gidlist_nr, old_gidlist.get()))
578 _error->FatalE("setgroups", "setgroups %u failed", 0);
7c8206bf 579}
1c5f7e5f 580pkgAcquire::RunResult pkgAcquire::Run(int PulseIntervall)
0a8a80e5 581{
95278287 582 _error->PushToStack();
7c8206bf
DK
583 CheckDropPrivsMustBeDisabled(*this);
584
8b89e57f
AL
585 Running = true;
586
0a8a80e5
AL
587 for (Queue *I = Queues; I != 0; I = I->Next)
588 I->Startup();
589
b98f2859
AL
590 if (Log != 0)
591 Log->Start();
592
024d1123
AL
593 bool WasCancelled = false;
594
0a8a80e5 595 // Run till all things have been acquired
8267fe24
AL
596 struct timeval tv;
597 tv.tv_sec = 0;
1c5f7e5f 598 tv.tv_usec = PulseIntervall;
0a8a80e5
AL
599 while (ToFetch > 0)
600 {
601 fd_set RFds;
602 fd_set WFds;
603 int Highest = 0;
604 FD_ZERO(&RFds);
605 FD_ZERO(&WFds);
606 SetFds(Highest,&RFds,&WFds);
607
b0db36b1
AL
608 int Res;
609 do
610 {
611 Res = select(Highest+1,&RFds,&WFds,0,&tv);
612 }
613 while (Res < 0 && errno == EINTR);
614
8267fe24 615 if (Res < 0)
8b89e57f 616 {
8267fe24
AL
617 _error->Errno("select","Select has failed");
618 break;
8b89e57f 619 }
95278287 620
a416a90b
MV
621 if(RunFdsSane(&RFds,&WFds) == false)
622 break;
95278287 623
8267fe24
AL
624 // Timeout, notify the log class
625 if (Res == 0 || (Log != 0 && Log->Update == true))
626 {
1c5f7e5f 627 tv.tv_usec = PulseIntervall;
8267fe24
AL
628 for (Worker *I = Workers; I != 0; I = I->NextAcquire)
629 I->Pulse();
024d1123
AL
630 if (Log != 0 && Log->Pulse(this) == false)
631 {
632 WasCancelled = true;
633 break;
634 }
8267fe24 635 }
0a8a80e5 636 }
be4401bf 637
b98f2859
AL
638 if (Log != 0)
639 Log->Stop();
640
be4401bf
AL
641 // Shut down the acquire bits
642 Running = false;
0a8a80e5 643 for (Queue *I = Queues; I != 0; I = I->Next)
8e5fc8f5 644 I->Shutdown(false);
0a8a80e5 645
ab559b35 646 // Shut down the items
f7f0d6c7 647 for (ItemIterator I = Items.begin(); I != Items.end(); ++I)
95278287
DK
648 (*I)->Finished();
649
650 bool const newError = _error->PendingError();
651 _error->MergeWithStack();
652 if (newError)
024d1123
AL
653 return Failed;
654 if (WasCancelled)
655 return Cancelled;
656 return Continue;
93bf083d
AL
657}
658 /*}}}*/
be4401bf 659// Acquire::Bump - Called when an item is dequeued /*{{{*/
93bf083d
AL
660// ---------------------------------------------------------------------
661/* This routine bumps idle queues in hopes that they will be able to fetch
662 the dequeued item */
663void pkgAcquire::Bump()
664{
be4401bf
AL
665 for (Queue *I = Queues; I != 0; I = I->Next)
666 I->Bump();
0a8a80e5
AL
667}
668 /*}}}*/
8267fe24
AL
669// Acquire::WorkerStep - Step to the next worker /*{{{*/
670// ---------------------------------------------------------------------
671/* Not inlined to advoid including acquire-worker.h */
672pkgAcquire::Worker *pkgAcquire::WorkerStep(Worker *I)
673{
674 return I->NextAcquire;
d3e8fbb3 675}
8267fe24 676 /*}}}*/
a6568219 677// Acquire::Clean - Cleans a directory /*{{{*/
7a7fa5f0
AL
678// ---------------------------------------------------------------------
679/* This is a bit simplistic, it looks at every file in the dir and sees
680 if it is part of the download set. */
681bool pkgAcquire::Clean(string Dir)
682{
95b5f6c1
DK
683 // non-existing directories are by definition clean…
684 if (DirectoryExists(Dir) == false)
685 return true;
686
10ecfe4f
MV
687 if(Dir == "/")
688 return _error->Error(_("Clean of %s is not supported"), Dir.c_str());
689
7a7fa5f0
AL
690 DIR *D = opendir(Dir.c_str());
691 if (D == 0)
b2e465d6 692 return _error->Errno("opendir",_("Unable to read %s"),Dir.c_str());
7a7fa5f0
AL
693
694 string StartDir = SafeGetCWD();
695 if (chdir(Dir.c_str()) != 0)
696 {
697 closedir(D);
b2e465d6 698 return _error->Errno("chdir",_("Unable to change to %s"),Dir.c_str());
7a7fa5f0
AL
699 }
700
701 for (struct dirent *Dir = readdir(D); Dir != 0; Dir = readdir(D))
702 {
703 // Skip some files..
704 if (strcmp(Dir->d_name,"lock") == 0 ||
705 strcmp(Dir->d_name,"partial") == 0 ||
6aef1942 706 strcmp(Dir->d_name,"lost+found") == 0 ||
7a7fa5f0
AL
707 strcmp(Dir->d_name,".") == 0 ||
708 strcmp(Dir->d_name,"..") == 0)
709 continue;
710
711 // Look in the get list
b4fc9b6f 712 ItemCIterator I = Items.begin();
f7f0d6c7 713 for (; I != Items.end(); ++I)
7a7fa5f0
AL
714 if (flNotDir((*I)->DestFile) == Dir->d_name)
715 break;
716
717 // Nothing found, nuke it
718 if (I == Items.end())
ce1f3a2c 719 RemoveFile("Clean", Dir->d_name);
7a7fa5f0
AL
720 };
721
7a7fa5f0 722 closedir(D);
3c8cda8b
MV
723 if (chdir(StartDir.c_str()) != 0)
724 return _error->Errno("chdir",_("Unable to change to %s"),StartDir.c_str());
7a7fa5f0
AL
725 return true;
726}
727 /*}}}*/
a6568219
AL
728// Acquire::TotalNeeded - Number of bytes to fetch /*{{{*/
729// ---------------------------------------------------------------------
730/* This is the total number of bytes needed */
a02db58f 731APT_PURE unsigned long long pkgAcquire::TotalNeeded()
a6568219 732{
5a978348 733 return std::accumulate(ItemsBegin(), ItemsEnd(), 0ull,
ae732225
DK
734 [](unsigned long long const T, Item const * const I) {
735 return T + I->FileSize;
736 });
a6568219
AL
737}
738 /*}}}*/
739// Acquire::FetchNeeded - Number of bytes needed to get /*{{{*/
740// ---------------------------------------------------------------------
741/* This is the number of bytes that is not local */
a02db58f 742APT_PURE unsigned long long pkgAcquire::FetchNeeded()
a6568219 743{
5a978348 744 return std::accumulate(ItemsBegin(), ItemsEnd(), 0llu,
ae732225
DK
745 [](unsigned long long const T, Item const * const I) {
746 if (I->Local == false)
747 return T + I->FileSize;
748 else
749 return T;
750 });
a6568219
AL
751}
752 /*}}}*/
6b1ff003
AL
753// Acquire::PartialPresent - Number of partial bytes we already have /*{{{*/
754// ---------------------------------------------------------------------
755/* This is the number of bytes that is not local */
a02db58f 756APT_PURE unsigned long long pkgAcquire::PartialPresent()
6b1ff003 757{
5a978348 758 return std::accumulate(ItemsBegin(), ItemsEnd(), 0llu,
ae732225
DK
759 [](unsigned long long const T, Item const * const I) {
760 if (I->Local == false)
761 return T + I->PartialSize;
762 else
763 return T;
764 });
6b1ff003 765}
92fcbfc1 766 /*}}}*/
8e5fc8f5 767// Acquire::UriBegin - Start iterator for the uri list /*{{{*/
f7a08e33
AL
768// ---------------------------------------------------------------------
769/* */
770pkgAcquire::UriIterator pkgAcquire::UriBegin()
771{
772 return UriIterator(Queues);
773}
774 /*}}}*/
8e5fc8f5 775// Acquire::UriEnd - End iterator for the uri list /*{{{*/
f7a08e33
AL
776// ---------------------------------------------------------------------
777/* */
778pkgAcquire::UriIterator pkgAcquire::UriEnd()
779{
780 return UriIterator(0);
781}
782 /*}}}*/
e331f6ed
AL
783// Acquire::MethodConfig::MethodConfig - Constructor /*{{{*/
784// ---------------------------------------------------------------------
785/* */
25613a61
DK
786pkgAcquire::MethodConfig::MethodConfig() : d(NULL), Next(0), SingleInstance(false),
787 Pipeline(false), SendConfig(false), LocalOnly(false), NeedsCleanup(false),
788 Removable(false)
e331f6ed 789{
e331f6ed
AL
790}
791 /*}}}*/
0a8a80e5
AL
792// Queue::Queue - Constructor /*{{{*/
793// ---------------------------------------------------------------------
794/* */
e8afd168
DK
795pkgAcquire::Queue::Queue(string const &name,pkgAcquire * const owner) : d(NULL), Next(0),
796 Name(name), Items(0), Workers(0), Owner(owner), PipeDepth(0), MaxPipeDepth(1)
0a8a80e5 797{
0a8a80e5
AL
798}
799 /*}}}*/
800// Queue::~Queue - Destructor /*{{{*/
801// ---------------------------------------------------------------------
802/* */
803pkgAcquire::Queue::~Queue()
804{
8e5fc8f5 805 Shutdown(true);
0a8a80e5
AL
806
807 while (Items != 0)
808 {
809 QItem *Jnk = Items;
810 Items = Items->Next;
811 delete Jnk;
812 }
813}
814 /*}}}*/
815// Queue::Enqueue - Queue an item to the queue /*{{{*/
816// ---------------------------------------------------------------------
817/* */
c03462c6 818bool pkgAcquire::Queue::Enqueue(ItemDesc &Item)
0a8a80e5 819{
7a1b1f8b 820 QItem **I = &Items;
c03462c6 821 // move to the end of the queue and check for duplicates here
9d2a8a73 822 HashStringList const hsl = Item.Owner->GetExpectedHashes();
c03462c6 823 for (; *I != 0; I = &(*I)->Next)
9d2a8a73 824 if (Item.URI == (*I)->URI || hsl == (*I)->Owner->GetExpectedHashes())
c03462c6 825 {
9d2a8a73
DK
826 if (_config->FindB("Debug::pkgAcquire::Worker",false) == true)
827 std::cerr << " @ Queue: Action combined for " << Item.URI << " and " << (*I)->URI << std::endl;
08ea7806
DK
828 (*I)->Owners.push_back(Item.Owner);
829 Item.Owner->Status = (*I)->Owner->Status;
c03462c6
MV
830 return false;
831 }
832
0a8a80e5 833 // Create a new item
7a1b1f8b
AL
834 QItem *Itm = new QItem;
835 *Itm = Item;
836 Itm->Next = 0;
837 *I = Itm;
0a8a80e5 838
8267fe24 839 Item.Owner->QueueCounter++;
93bf083d
AL
840 if (Items->Next == 0)
841 Cycle();
c03462c6 842 return true;
0a8a80e5
AL
843}
844 /*}}}*/
c88edf1d 845// Queue::Dequeue - Remove an item from the queue /*{{{*/
0a8a80e5 846// ---------------------------------------------------------------------
b185acc2 847/* We return true if we hit something */
bfd22fc0 848bool pkgAcquire::Queue::Dequeue(Item *Owner)
0a8a80e5 849{
b185acc2
AL
850 if (Owner->Status == pkgAcquire::Item::StatFetching)
851 return _error->Error("Tried to dequeue a fetching object");
08ea7806 852
bfd22fc0 853 bool Res = false;
08ea7806 854
0a8a80e5
AL
855 QItem **I = &Items;
856 for (; *I != 0;)
857 {
08ea7806 858 if (Owner == (*I)->Owner)
0a8a80e5
AL
859 {
860 QItem *Jnk= *I;
861 *I = (*I)->Next;
862 Owner->QueueCounter--;
863 delete Jnk;
bfd22fc0 864 Res = true;
0a8a80e5
AL
865 }
866 else
867 I = &(*I)->Next;
868 }
08ea7806 869
bfd22fc0 870 return Res;
0a8a80e5
AL
871}
872 /*}}}*/
873// Queue::Startup - Start the worker processes /*{{{*/
874// ---------------------------------------------------------------------
8e5fc8f5
AL
875/* It is possible for this to be called with a pre-existing set of
876 workers. */
0a8a80e5
AL
877bool pkgAcquire::Queue::Startup()
878{
8e5fc8f5
AL
879 if (Workers == 0)
880 {
881 URI U(Name);
882 pkgAcquire::MethodConfig *Cnf = Owner->GetConfig(U.Access);
883 if (Cnf == 0)
884 return false;
885
886 Workers = new Worker(this,Cnf,Owner->Log);
887 Owner->Add(Workers);
888 if (Workers->Start() == false)
889 return false;
890
891 /* When pipelining we commit 10 items. This needs to change when we
892 added other source retry to have cycle maintain a pipeline depth
893 on its own. */
894 if (Cnf->Pipeline == true)
6ce72612 895 MaxPipeDepth = _config->FindI("Acquire::Max-Pipeline-Depth",10);
8e5fc8f5
AL
896 else
897 MaxPipeDepth = 1;
898 }
5cb5d8dc 899
93bf083d 900 return Cycle();
0a8a80e5
AL
901}
902 /*}}}*/
903// Queue::Shutdown - Shutdown the worker processes /*{{{*/
904// ---------------------------------------------------------------------
8e5fc8f5
AL
905/* If final is true then all workers are eliminated, otherwise only workers
906 that do not need cleanup are removed */
907bool pkgAcquire::Queue::Shutdown(bool Final)
0a8a80e5
AL
908{
909 // Delete all of the workers
8e5fc8f5
AL
910 pkgAcquire::Worker **Cur = &Workers;
911 while (*Cur != 0)
0a8a80e5 912 {
8e5fc8f5
AL
913 pkgAcquire::Worker *Jnk = *Cur;
914 if (Final == true || Jnk->GetConf()->NeedsCleanup == false)
915 {
916 *Cur = Jnk->NextQueue;
917 Owner->Remove(Jnk);
918 delete Jnk;
919 }
920 else
921 Cur = &(*Cur)->NextQueue;
0a8a80e5
AL
922 }
923
924 return true;
3b5421b4
AL
925}
926 /*}}}*/
7d8afa39 927// Queue::FindItem - Find a URI in the item list /*{{{*/
c88edf1d
AL
928// ---------------------------------------------------------------------
929/* */
930pkgAcquire::Queue::QItem *pkgAcquire::Queue::FindItem(string URI,pkgAcquire::Worker *Owner)
931{
932 for (QItem *I = Items; I != 0; I = I->Next)
933 if (I->URI == URI && I->Worker == Owner)
934 return I;
935 return 0;
936}
937 /*}}}*/
938// Queue::ItemDone - Item has been completed /*{{{*/
939// ---------------------------------------------------------------------
940/* The worker signals this which causes the item to be removed from the
93bf083d
AL
941 queue. If this is the last queue instance then it is removed from the
942 main queue too.*/
c88edf1d
AL
943bool pkgAcquire::Queue::ItemDone(QItem *Itm)
944{
b185acc2 945 PipeDepth--;
08ea7806
DK
946 for (QItem::owner_iterator O = Itm->Owners.begin(); O != Itm->Owners.end(); ++O)
947 {
948 if ((*O)->Status == pkgAcquire::Item::StatFetching)
949 (*O)->Status = pkgAcquire::Item::StatDone;
950 }
951
93bf083d
AL
952 if (Itm->Owner->QueueCounter <= 1)
953 Owner->Dequeue(Itm->Owner);
954 else
955 {
956 Dequeue(Itm->Owner);
957 Owner->Bump();
958 }
08ea7806 959
93bf083d
AL
960 return Cycle();
961}
962 /*}}}*/
963// Queue::Cycle - Queue new items into the method /*{{{*/
964// ---------------------------------------------------------------------
b185acc2
AL
965/* This locates a new idle item and sends it to the worker. If pipelining
966 is enabled then it keeps the pipe full. */
93bf083d
AL
967bool pkgAcquire::Queue::Cycle()
968{
969 if (Items == 0 || Workers == 0)
c88edf1d
AL
970 return true;
971
e7432370
AL
972 if (PipeDepth < 0)
973 return _error->Error("Pipedepth failure");
08ea7806 974
93bf083d
AL
975 // Look for a queable item
976 QItem *I = Items;
e7432370 977 while (PipeDepth < (signed)MaxPipeDepth)
b185acc2
AL
978 {
979 for (; I != 0; I = I->Next)
980 if (I->Owner->Status == pkgAcquire::Item::StatIdle)
981 break;
08ea7806 982
b185acc2
AL
983 // Nothing to do, queue is idle.
984 if (I == 0)
985 return true;
08ea7806 986
b185acc2 987 I->Worker = Workers;
ae732225
DK
988 for (auto const &O: I->Owners)
989 O->Status = pkgAcquire::Item::StatFetching;
e7432370 990 PipeDepth++;
b185acc2
AL
991 if (Workers->QueueItem(I) == false)
992 return false;
993 }
08ea7806 994
b185acc2 995 return true;
c88edf1d
AL
996}
997 /*}}}*/
be4401bf
AL
998// Queue::Bump - Fetch any pending objects if we are idle /*{{{*/
999// ---------------------------------------------------------------------
b185acc2 1000/* This is called when an item in multiple queues is dequeued */
be4401bf
AL
1001void pkgAcquire::Queue::Bump()
1002{
b185acc2 1003 Cycle();
be4401bf
AL
1004}
1005 /*}}}*/
08ea7806
DK
1006HashStringList pkgAcquire::Queue::QItem::GetExpectedHashes() const /*{{{*/
1007{
1008 /* each Item can have multiple owners and each owner might have different
1009 hashes, even if that is unlikely in practice and if so at least some
1010 owners will later fail. There is one situation through which is not a
1011 failure and still needs this handling: Two owners who expect the same
1012 file, but one owner only knows the SHA1 while the other only knows SHA256. */
1013 HashStringList superhsl;
1014 for (pkgAcquire::Queue::QItem::owner_iterator O = Owners.begin(); O != Owners.end(); ++O)
1015 {
1016 HashStringList const hsl = (*O)->GetExpectedHashes();
1017 if (hsl.usable() == false)
1018 continue;
1019 if (superhsl.usable() == false)
1020 superhsl = hsl;
1021 else
1022 {
1023 // we merge both lists - if we find disagreement send no hashes
1024 HashStringList::const_iterator hs = hsl.begin();
1025 for (; hs != hsl.end(); ++hs)
1026 if (superhsl.push_back(*hs) == false)
1027 break;
1028 if (hs != hsl.end())
1029 {
1030 superhsl.clear();
1031 break;
1032 }
1033 }
1034 }
1035 return superhsl;
1036}
1037 /*}}}*/
1038APT_PURE unsigned long long pkgAcquire::Queue::QItem::GetMaximumSize() const /*{{{*/
1039{
1040 unsigned long long Maximum = std::numeric_limits<unsigned long long>::max();
ae732225 1041 for (auto const &O: Owners)
08ea7806 1042 {
ae732225 1043 if (O->FileSize == 0)
08ea7806 1044 continue;
ae732225 1045 Maximum = std::min(Maximum, O->FileSize);
08ea7806
DK
1046 }
1047 if (Maximum == std::numeric_limits<unsigned long long>::max())
1048 return 0;
1049 return Maximum;
1050}
1051 /*}}}*/
1052void pkgAcquire::Queue::QItem::SyncDestinationFiles() const /*{{{*/
1053{
1054 /* ensure that the first owner has the best partial file of all and
1055 the rest have (potentially dangling) symlinks to it so that
1056 everything (like progress reporting) finds it easily */
1057 std::string superfile = Owner->DestFile;
1058 off_t supersize = 0;
1059 for (pkgAcquire::Queue::QItem::owner_iterator O = Owners.begin(); O != Owners.end(); ++O)
1060 {
1061 if ((*O)->DestFile == superfile)
1062 continue;
1063 struct stat file;
1064 if (lstat((*O)->DestFile.c_str(),&file) == 0)
1065 {
1066 if ((file.st_mode & S_IFREG) == 0)
ce1f3a2c 1067 RemoveFile("SyncDestinationFiles", (*O)->DestFile);
08ea7806
DK
1068 else if (supersize < file.st_size)
1069 {
1070 supersize = file.st_size;
ce1f3a2c 1071 RemoveFile("SyncDestinationFiles", superfile);
08ea7806
DK
1072 rename((*O)->DestFile.c_str(), superfile.c_str());
1073 }
1074 else
ce1f3a2c 1075 RemoveFile("SyncDestinationFiles", (*O)->DestFile);
08ea7806
DK
1076 if (symlink(superfile.c_str(), (*O)->DestFile.c_str()) != 0)
1077 {
1078 ; // not a problem per-se and no real alternative
1079 }
1080 }
1081 }
1082}
1083 /*}}}*/
1084std::string pkgAcquire::Queue::QItem::Custom600Headers() const /*{{{*/
1085{
1086 /* The others are relatively easy to merge, but this one?
1087 Lets not merge and see how far we can run with it…
1088 Likely, nobody will ever notice as all the items will
1089 be of the same class and hence generate the same headers. */
1090 return Owner->Custom600Headers();
1091}
1092 /*}}}*/
1093
b98f2859
AL
1094// AcquireStatus::pkgAcquireStatus - Constructor /*{{{*/
1095// ---------------------------------------------------------------------
1096/* */
533fe3d1 1097pkgAcquireStatus::pkgAcquireStatus() : d(NULL), Percent(-1), Update(true), MorePulses(false)
b98f2859
AL
1098{
1099 Start();
1100}
1101 /*}}}*/
1102// AcquireStatus::Pulse - Called periodically /*{{{*/
1103// ---------------------------------------------------------------------
1104/* This computes some internal state variables for the derived classes to
1105 use. It generates the current downloaded bytes and total bytes to download
1106 as well as the current CPS estimate. */
024d1123 1107bool pkgAcquireStatus::Pulse(pkgAcquire *Owner)
b98f2859
AL
1108{
1109 TotalBytes = 0;
1110 CurrentBytes = 0;
d568ed2d
AL
1111 TotalItems = 0;
1112 CurrentItems = 0;
b98f2859
AL
1113
1114 // Compute the total number of bytes to fetch
1115 unsigned int Unknown = 0;
1116 unsigned int Count = 0;
c6e9cc58
MV
1117 bool UnfetchedReleaseFiles = false;
1118 for (pkgAcquire::ItemCIterator I = Owner->ItemsBegin();
1119 I != Owner->ItemsEnd();
f7f0d6c7 1120 ++I, ++Count)
b98f2859 1121 {
d568ed2d
AL
1122 TotalItems++;
1123 if ((*I)->Status == pkgAcquire::Item::StatDone)
f7f0d6c7 1124 ++CurrentItems;
d568ed2d 1125
a6568219
AL
1126 // Totally ignore local items
1127 if ((*I)->Local == true)
1128 continue;
b2e465d6 1129
d0cfa8ad
MV
1130 // see if the method tells us to expect more
1131 TotalItems += (*I)->ExpectedAdditionalItems;
1132
c6e9cc58
MV
1133 // check if there are unfetched Release files
1134 if ((*I)->Complete == false && (*I)->ExpectedAdditionalItems > 0)
1135 UnfetchedReleaseFiles = true;
1136
b98f2859
AL
1137 TotalBytes += (*I)->FileSize;
1138 if ((*I)->Complete == true)
1139 CurrentBytes += (*I)->FileSize;
1140 if ((*I)->FileSize == 0 && (*I)->Complete == false)
f7f0d6c7 1141 ++Unknown;
b98f2859
AL
1142 }
1143
1144 // Compute the current completion
dbbc5494 1145 unsigned long long ResumeSize = 0;
b98f2859
AL
1146 for (pkgAcquire::Worker *I = Owner->WorkersBegin(); I != 0;
1147 I = Owner->WorkerStep(I))
c62f7898 1148 {
b98f2859 1149 if (I->CurrentItem != 0 && I->CurrentItem->Owner->Complete == false)
aa0e1101
AL
1150 {
1151 CurrentBytes += I->CurrentSize;
1152 ResumeSize += I->ResumePoint;
08ea7806 1153
aa0e1101 1154 // Files with unknown size always have 100% completion
08ea7806 1155 if (I->CurrentItem->Owner->FileSize == 0 &&
aa0e1101
AL
1156 I->CurrentItem->Owner->Complete == false)
1157 TotalBytes += I->CurrentSize;
1158 }
c62f7898 1159 }
aa0e1101 1160
b98f2859
AL
1161 // Normalize the figures and account for unknown size downloads
1162 if (TotalBytes <= 0)
1163 TotalBytes = 1;
1164 if (Unknown == Count)
1165 TotalBytes = Unknown;
18ef0a78
AL
1166
1167 // Wha?! Is not supposed to happen.
1168 if (CurrentBytes > TotalBytes)
1169 CurrentBytes = TotalBytes;
96c6cab1
MV
1170
1171 // debug
1172 if (_config->FindB("Debug::acquire::progress", false) == true)
1173 std::clog << " Bytes: "
1174 << SizeToStr(CurrentBytes) << " / " << SizeToStr(TotalBytes)
1175 << std::endl;
b98f2859
AL
1176
1177 // Compute the CPS
1178 struct timeval NewTime;
1179 gettimeofday(&NewTime,0);
2ec1674d 1180 if ((NewTime.tv_sec - Time.tv_sec == 6 && NewTime.tv_usec > Time.tv_usec) ||
b98f2859
AL
1181 NewTime.tv_sec - Time.tv_sec > 6)
1182 {
f17ac097
AL
1183 double Delta = NewTime.tv_sec - Time.tv_sec +
1184 (NewTime.tv_usec - Time.tv_usec)/1000000.0;
b98f2859 1185
b98f2859 1186 // Compute the CPS value
f17ac097 1187 if (Delta < 0.01)
e331f6ed
AL
1188 CurrentCPS = 0;
1189 else
aa0e1101
AL
1190 CurrentCPS = ((CurrentBytes - ResumeSize) - LastBytes)/Delta;
1191 LastBytes = CurrentBytes - ResumeSize;
dbbc5494 1192 ElapsedTime = (unsigned long long)Delta;
b98f2859
AL
1193 Time = NewTime;
1194 }
024d1123 1195
533fe3d1 1196 double const OldPercent = Percent;
c6e9cc58
MV
1197 // calculate the percentage, if we have too little data assume 1%
1198 if (TotalBytes > 0 && UnfetchedReleaseFiles)
96c6cab1 1199 Percent = 0;
533fe3d1 1200 else
96c6cab1 1201 // use both files and bytes because bytes can be unreliable
533fe3d1 1202 Percent = (0.8 * (CurrentBytes/float(TotalBytes)*100.0) +
96c6cab1 1203 0.2 * (CurrentItems/float(TotalItems)*100.0));
533fe3d1
DK
1204 double const DiffPercent = Percent - OldPercent;
1205 if (DiffPercent < 0.001 && _config->FindB("Acquire::Progress::Diffpercent", false) == true)
1206 return true;
96c6cab1 1207
75ef8f14
MV
1208 int fd = _config->FindI("APT::Status-Fd",-1);
1209 if(fd > 0)
1210 {
1211 ostringstream status;
1212
1213 char msg[200];
1214 long i = CurrentItems < TotalItems ? CurrentItems + 1 : CurrentItems;
c033d415
MV
1215 unsigned long long ETA = 0;
1216 if(CurrentCPS > 0)
1217 ETA = (TotalBytes - CurrentBytes) / CurrentCPS;
75ef8f14 1218
1e8b4c0f
MV
1219 // only show the ETA if it makes sense
1220 if (ETA > 0 && ETA < 172800 /* two days */ )
0c508b03 1221 snprintf(msg,sizeof(msg), _("Retrieving file %li of %li (%s remaining)"), i, TotalItems, TimeToStr(ETA).c_str());
1e8b4c0f 1222 else
0c508b03 1223 snprintf(msg,sizeof(msg), _("Retrieving file %li of %li"), i, TotalItems);
533fe3d1 1224
75ef8f14
MV
1225 // build the status str
1226 status << "dlstatus:" << i
96c6cab1 1227 << ":" << std::setprecision(3) << Percent
533fe3d1 1228 << ":" << msg
d0cfa8ad 1229 << endl;
31bda500
DK
1230
1231 std::string const dlstatus = status.str();
d68d65ad 1232 FileFd::Write(fd, dlstatus.c_str(), dlstatus.size());
75ef8f14
MV
1233 }
1234
024d1123 1235 return true;
b98f2859
AL
1236}
1237 /*}}}*/
1238// AcquireStatus::Start - Called when the download is started /*{{{*/
1239// ---------------------------------------------------------------------
1240/* We just reset the counters */
1241void pkgAcquireStatus::Start()
1242{
1243 gettimeofday(&Time,0);
1244 gettimeofday(&StartTime,0);
1245 LastBytes = 0;
1246 CurrentCPS = 0;
1247 CurrentBytes = 0;
1248 TotalBytes = 0;
1249 FetchedBytes = 0;
1250 ElapsedTime = 0;
d568ed2d
AL
1251 TotalItems = 0;
1252 CurrentItems = 0;
b98f2859
AL
1253}
1254 /*}}}*/
a6568219 1255// AcquireStatus::Stop - Finished downloading /*{{{*/
b98f2859
AL
1256// ---------------------------------------------------------------------
1257/* This accurately computes the elapsed time and the total overall CPS. */
1258void pkgAcquireStatus::Stop()
1259{
1260 // Compute the CPS and elapsed time
1261 struct timeval NewTime;
1262 gettimeofday(&NewTime,0);
1263
31a0531d
AL
1264 double Delta = NewTime.tv_sec - StartTime.tv_sec +
1265 (NewTime.tv_usec - StartTime.tv_usec)/1000000.0;
b98f2859 1266
b98f2859 1267 // Compute the CPS value
31a0531d 1268 if (Delta < 0.01)
e331f6ed
AL
1269 CurrentCPS = 0;
1270 else
31a0531d 1271 CurrentCPS = FetchedBytes/Delta;
b98f2859 1272 LastBytes = CurrentBytes;
dbbc5494 1273 ElapsedTime = (unsigned long long)Delta;
b98f2859
AL
1274}
1275 /*}}}*/
1276// AcquireStatus::Fetched - Called when a byte set has been fetched /*{{{*/
1277// ---------------------------------------------------------------------
1278/* This is used to get accurate final transfer rate reporting. */
73da43e9 1279void pkgAcquireStatus::Fetched(unsigned long long Size,unsigned long long Resume)
93274b8d 1280{
b98f2859
AL
1281 FetchedBytes += Size - Resume;
1282}
1283 /*}}}*/
862bafea 1284
c8a4ce6c
DK
1285pkgAcquire::UriIterator::UriIterator(pkgAcquire::Queue *Q) : d(NULL), CurQ(Q), CurItem(0)
1286{
1287 while (CurItem == 0 && CurQ != 0)
1288 {
1289 CurItem = CurQ->Items;
1290 CurQ = CurQ->Next;
1291 }
1292}
1293
9d653a6d
DK
1294APT_CONST pkgAcquire::UriIterator::~UriIterator() {}
1295APT_CONST pkgAcquire::MethodConfig::~MethodConfig() {}
1296APT_CONST pkgAcquireStatus::~pkgAcquireStatus() {}