]> git.saurik.com Git - apt.git/blame - apt-pkg/acquire.cc
improve code & doc for aquire weak/loop failing
[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. */
10443549 272static bool CheckForBadItemAndFailIt(pkgAcquire::Item * const Item,
2e2865ae
DK
273 pkgAcquire::MethodConfig const * const Config, pkgAcquireStatus * const Log)
274{
275 auto SavedDesc = Item->GetItemDesc();
276 if (Item->IsRedirectionLoop(SavedDesc.URI))
277 {
278 std::string const Message = "400 URI Failure"
279 "\nURI: " + SavedDesc.URI +
280 "\nFilename: " + Item->DestFile +
281 "\nFailReason: RedirectionLoop";
282
283 Item->Status = pkgAcquire::Item::StatError;
284 Item->Failed(Message, Config);
285 if (Log != nullptr)
286 Log->Fail(SavedDesc);
287 return true;
288 }
289
290 HashStringList const hsl = Item->GetExpectedHashes();
291 if (hsl.usable() == false && Item->HashesRequired() &&
292 _config->Exists("Acquire::ForceHash") == false)
293 {
294 std::string const Message = "400 URI Failure"
295 "\nURI: " + SavedDesc.URI +
296 "\nFilename: " + Item->DestFile +
297 "\nFailReason: WeakHashSums";
298
299 auto SavedDesc = Item->GetItemDesc();
300 Item->Status = pkgAcquire::Item::StatAuthError;
301 Item->Failed(Message, Config);
302 if (Log != nullptr)
303 Log->Fail(SavedDesc);
304 return true;
305 }
306 return false;
307}
8267fe24 308void pkgAcquire::Enqueue(ItemDesc &Item)
0118833a 309{
0a8a80e5 310 // Determine which queue to put the item in
e331f6ed
AL
311 const MethodConfig *Config;
312 string Name = QueueName(Item.URI,Config);
0a8a80e5
AL
313 if (Name.empty() == true)
314 return;
315
2e2865ae
DK
316 /* the check for running avoids that we produce errors
317 in logging before we actually have started, which would
318 be easier to implement but would confuse users/implementations
319 so we check the items skipped here in #Startup */
10443549 320 if (Running && CheckForBadItemAndFailIt(Item.Owner, Config, Log))
2e2865ae
DK
321 return;
322
0a8a80e5
AL
323 // Find the queue structure
324 Queue *I = Queues;
325 for (; I != 0 && I->Name != Name; I = I->Next);
326 if (I == 0)
327 {
328 I = new Queue(Name,this);
329 I->Next = Queues;
330 Queues = I;
93bf083d
AL
331
332 if (Running == true)
333 I->Startup();
0a8a80e5 334 }
bfd22fc0 335
e331f6ed
AL
336 // See if this is a local only URI
337 if (Config->LocalOnly == true && Item.Owner->Complete == false)
338 Item.Owner->Local = true;
8267fe24 339 Item.Owner->Status = Item::StatIdle;
0a8a80e5
AL
340
341 // Queue it into the named queue
c03462c6
MV
342 if(I->Enqueue(Item))
343 ToFetch++;
344
0a8a80e5
AL
345 // Some trace stuff
346 if (Debug == true)
347 {
8267fe24
AL
348 clog << "Fetching " << Item.URI << endl;
349 clog << " to " << Item.Owner->DestFile << endl;
e331f6ed 350 clog << " Queue is: " << Name << endl;
0a8a80e5 351 }
3b5421b4
AL
352}
353 /*}}}*/
0a8a80e5 354// Acquire::Dequeue - Remove an item from all queues /*{{{*/
3b5421b4 355// ---------------------------------------------------------------------
93bf083d
AL
356/* This is called when an item is finished being fetched. It removes it
357 from all the queues */
0a8a80e5
AL
358void pkgAcquire::Dequeue(Item *Itm)
359{
360 Queue *I = Queues;
bfd22fc0 361 bool Res = false;
93bf083d
AL
362 if (Debug == true)
363 clog << "Dequeuing " << Itm->DestFile << endl;
5674f6b3
RG
364
365 for (; I != 0; I = I->Next)
366 {
367 if (I->Dequeue(Itm))
368 {
369 Res = true;
370 if (Debug == true)
371 clog << "Dequeued from " << I->Name << endl;
372 }
373 }
374
bfd22fc0
AL
375 if (Res == true)
376 ToFetch--;
0a8a80e5
AL
377}
378 /*}}}*/
379// Acquire::QueueName - Return the name of the queue for this URI /*{{{*/
380// ---------------------------------------------------------------------
381/* The string returned depends on the configuration settings and the
382 method parameters. Given something like http://foo.org/bar it can
383 return http://foo.org or http */
e331f6ed 384string pkgAcquire::QueueName(string Uri,MethodConfig const *&Config)
3b5421b4 385{
93bf083d
AL
386 URI U(Uri);
387
e331f6ed 388 Config = GetConfig(U.Access);
0a8a80e5
AL
389 if (Config == 0)
390 return string();
391
392 /* Single-Instance methods get exactly one queue per URI. This is
393 also used for the Access queue method */
394 if (Config->SingleInstance == true || QueueMode == QueueAccess)
5674f6b3
RG
395 return U.Access;
396
b89cd2e3 397 string AccessSchema = U.Access + ':';
4aa6ebf6 398 string FullQueueName;
b89cd2e3
JAK
399
400 if (U.Host.empty())
401 {
4aa6ebf6
DK
402 long existing = 0;
403 // check how many queues exist already and reuse empty ones
404 for (Queue const *I = Queues; I != 0; I = I->Next)
405 if (I->Name.compare(0, AccessSchema.length(), AccessSchema) == 0)
406 {
407 if (I->Items == nullptr)
408 return I->Name;
409 ++existing;
410 }
411
b89cd2e3
JAK
412#ifdef _SC_NPROCESSORS_ONLN
413 long cpuCount = sysconf(_SC_NPROCESSORS_ONLN) * 2;
414#else
4aa6ebf6 415 long cpuCount = 10;
b89cd2e3 416#endif
4aa6ebf6 417 cpuCount = _config->FindI("Acquire::QueueHost::Limit", cpuCount);
b89cd2e3 418
4aa6ebf6
DK
419 if (cpuCount <= 0 || existing < cpuCount)
420 strprintf(FullQueueName, "%s%ld", AccessSchema.c_str(), existing);
421 else
422 {
423 long const randomQueue = random() % cpuCount;
424 strprintf(FullQueueName, "%s%ld", AccessSchema.c_str(), randomQueue);
b89cd2e3 425 }
4aa6ebf6
DK
426
427 if (Debug)
428 clog << "Chose random queue " << FullQueueName << " for " << Uri << endl;
b89cd2e3
JAK
429 } else
430 {
431 FullQueueName = AccessSchema + U.Host;
432 }
5674f6b3
RG
433 unsigned int Instances = 0, SchemaLength = AccessSchema.length();
434
435 Queue *I = Queues;
436 for (; I != 0; I = I->Next) {
437 // if the queue already exists, re-use it
438 if (I->Name == FullQueueName)
439 return FullQueueName;
440
441 if (I->Name.compare(0, SchemaLength, AccessSchema) == 0)
442 Instances++;
443 }
444
445 if (Debug) {
446 clog << "Found " << Instances << " instances of " << U.Access << endl;
447 }
448
449 if (Instances >= (unsigned int)_config->FindI("Acquire::QueueHost::Limit",10))
450 return U.Access;
93bf083d 451
5674f6b3 452 return FullQueueName;
0118833a
AL
453}
454 /*}}}*/
3b5421b4
AL
455// Acquire::GetConfig - Fetch the configuration information /*{{{*/
456// ---------------------------------------------------------------------
457/* This locates the configuration structure for an access method. If
458 a config structure cannot be found a Worker will be created to
459 retrieve it */
0a8a80e5 460pkgAcquire::MethodConfig *pkgAcquire::GetConfig(string Access)
3b5421b4
AL
461{
462 // Search for an existing config
463 MethodConfig *Conf;
464 for (Conf = Configs; Conf != 0; Conf = Conf->Next)
465 if (Conf->Access == Access)
466 return Conf;
467
468 // Create the new config class
469 Conf = new MethodConfig;
470 Conf->Access = Access;
471 Conf->Next = Configs;
472 Configs = Conf;
0118833a 473
3b5421b4
AL
474 // Create the worker to fetch the configuration
475 Worker Work(Conf);
476 if (Work.Start() == false)
477 return 0;
7c6e2dc7
MV
478
479 /* if a method uses DownloadLimit, we switch to SingleInstance mode */
4b65cc13 480 if(_config->FindI("Acquire::"+Access+"::Dl-Limit",0) > 0)
7c6e2dc7
MV
481 Conf->SingleInstance = true;
482
3b5421b4
AL
483 return Conf;
484}
485 /*}}}*/
0a8a80e5
AL
486// Acquire::SetFds - Deal with readable FDs /*{{{*/
487// ---------------------------------------------------------------------
488/* Collect FDs that have activity monitors into the fd sets */
489void pkgAcquire::SetFds(int &Fd,fd_set *RSet,fd_set *WSet)
490{
491 for (Worker *I = Workers; I != 0; I = I->NextAcquire)
492 {
493 if (I->InReady == true && I->InFd >= 0)
494 {
495 if (Fd < I->InFd)
496 Fd = I->InFd;
497 FD_SET(I->InFd,RSet);
498 }
499 if (I->OutReady == true && I->OutFd >= 0)
500 {
501 if (Fd < I->OutFd)
502 Fd = I->OutFd;
503 FD_SET(I->OutFd,WSet);
504 }
505 }
506}
507 /*}}}*/
a416a90b
MV
508// Acquire::RunFds - compatibility remove on next abi/api break /*{{{*/
509void pkgAcquire::RunFds(fd_set *RSet,fd_set *WSet)
510{
511 RunFdsSane(RSet, WSet);
124e6916 512}
a416a90b
MV
513 /*}}}*/
514// Acquire::RunFdsSane - Deal with active FDs /*{{{*/
0a8a80e5 515// ---------------------------------------------------------------------
93bf083d
AL
516/* Dispatch active FDs over to the proper workers. It is very important
517 that a worker never be erased while this is running! The queue class
518 should never erase a worker except during shutdown processing. */
a416a90b 519bool pkgAcquire::RunFdsSane(fd_set *RSet,fd_set *WSet)
0a8a80e5 520{
a416a90b
MV
521 bool Res = true;
522
0a8a80e5
AL
523 for (Worker *I = Workers; I != 0; I = I->NextAcquire)
524 {
525 if (I->InFd >= 0 && FD_ISSET(I->InFd,RSet) != 0)
a416a90b 526 Res &= I->InFdReady();
0a8a80e5 527 if (I->OutFd >= 0 && FD_ISSET(I->OutFd,WSet) != 0)
a416a90b 528 Res &= I->OutFdReady();
0a8a80e5 529 }
a416a90b
MV
530
531 return Res;
0a8a80e5
AL
532}
533 /*}}}*/
534// Acquire::Run - Run the fetch sequence /*{{{*/
535// ---------------------------------------------------------------------
536/* This runs the queues. It manages a select loop for all of the
537 Worker tasks. The workers interact with the queues and items to
538 manage the actual fetch. */
514a25cb
DK
539static bool IsAccessibleBySandboxUser(std::string const &filename, bool const ReadWrite)
540{
541 // you would think this is easily to answer with faccessat, right? Wrong!
542 // It e.g. gets groups wrong, so the only thing which works reliable is trying
543 // to open the file we want to open later on…
544 if (unlikely(filename.empty()))
545 return true;
546
547 if (ReadWrite == false)
548 {
549 errno = 0;
550 // can we read a file? Note that non-existing files are "fine"
551 int const fd = open(filename.c_str(), O_RDONLY | O_CLOEXEC);
552 if (fd == -1 && errno == EACCES)
553 return false;
554 close(fd);
555 return true;
556 }
557 else
558 {
559 // the file might not exist yet and even if it does we will fix permissions,
560 // so important is here just that the directory it is in allows that
561 std::string const dirname = flNotFile(filename);
562 if (unlikely(dirname.empty()))
563 return true;
564
565 char const * const filetag = ".apt-acquire-privs-test.XXXXXX";
566 std::string const tmpfile_tpl = flCombine(dirname, filetag);
567 std::unique_ptr<char, decltype(std::free) *> tmpfile { strdup(tmpfile_tpl.c_str()), std::free };
568 int const fd = mkstemp(tmpfile.get());
569 if (fd == -1 && errno == EACCES)
570 return false;
571 RemoveFile("IsAccessibleBySandboxUser", tmpfile.get());
572 close(fd);
573 return true;
574 }
575}
7c8206bf
DK
576static void CheckDropPrivsMustBeDisabled(pkgAcquire const &Fetcher)
577{
578 if(getuid() != 0)
579 return;
580
514a25cb
DK
581 std::string const SandboxUser = _config->Find("APT::Sandbox::User");
582 if (SandboxUser.empty() || SandboxUser == "root")
7c8206bf
DK
583 return;
584
585 struct passwd const * const pw = getpwnam(SandboxUser.c_str());
586 if (pw == NULL)
ef39c148
MV
587 {
588 _error->Warning(_("No sandbox user '%s' on the system, can not drop privileges"), SandboxUser.c_str());
589 _config->Set("APT::Sandbox::User", "");
7c8206bf 590 return;
ef39c148 591 }
7c8206bf 592
226c0f64
DK
593 gid_t const old_euid = geteuid();
594 gid_t const old_egid = getegid();
514a25cb
DK
595
596 long const ngroups_max = sysconf(_SC_NGROUPS_MAX);
597 std::unique_ptr<gid_t[]> old_gidlist(new gid_t[ngroups_max]);
598 if (unlikely(old_gidlist == NULL))
599 return;
600 ssize_t old_gidlist_nr;
601 if ((old_gidlist_nr = getgroups(ngroups_max, old_gidlist.get())) < 0)
602 {
603 _error->FatalE("getgroups", "getgroups %lu failed", ngroups_max);
604 old_gidlist[0] = 0;
605 old_gidlist_nr = 1;
606 }
607 if (setgroups(1, &pw->pw_gid))
608 _error->FatalE("setgroups", "setgroups %u failed", pw->pw_gid);
609
7c8206bf 610 if (setegid(pw->pw_gid) != 0)
514a25cb 611 _error->FatalE("setegid", "setegid %u failed", pw->pw_gid);
7c8206bf 612 if (seteuid(pw->pw_uid) != 0)
514a25cb 613 _error->FatalE("seteuid", "seteuid %u failed", pw->pw_uid);
7c8206bf 614
7c8206bf 615 for (pkgAcquire::ItemCIterator I = Fetcher.ItemsBegin();
69b76544 616 I != Fetcher.ItemsEnd(); ++I)
7c8206bf 617 {
226c0f64 618 // no need to drop privileges for a complete file
6fd4b4c0 619 if ((*I)->Complete == true || (*I)->Status != pkgAcquire::Item::StatIdle)
7c8206bf
DK
620 continue;
621
514a25cb
DK
622 // if destination file is inaccessible all hope is lost for privilege dropping
623 if (IsAccessibleBySandboxUser((*I)->DestFile, true) == false)
7c8206bf 624 {
7c8206bf 625 _error->WarningE("pkgAcquire::Run", _("Can't drop privileges for downloading as file '%s' couldn't be accessed by user '%s'."),
514a25cb 626 (*I)->DestFile.c_str(), SandboxUser.c_str());
7c8206bf
DK
627 _config->Set("APT::Sandbox::User", "");
628 break;
629 }
514a25cb
DK
630
631 // if its the source file (e.g. local sources) we might be lucky
632 // by dropping the dropping only for some methods.
633 URI const source = (*I)->DescURI();
634 if (source.Access == "file" || source.Access == "copy")
635 {
636 std::string const conf = "Binary::" + source.Access + "::APT::Sandbox::User";
637 if (_config->Exists(conf) == true)
638 continue;
639
640 if (IsAccessibleBySandboxUser(source.Path, false) == false)
641 {
642 _error->NoticeE("pkgAcquire::Run", _("Can't drop privileges for downloading as file '%s' couldn't be accessed by user '%s'."),
643 source.Path.c_str(), SandboxUser.c_str());
644 _config->CndSet("Binary::file::APT::Sandbox::User", "root");
645 _config->CndSet("Binary::copy::APT::Sandbox::User", "root");
646 }
647 }
7c8206bf
DK
648 }
649
226c0f64 650 if (seteuid(old_euid) != 0)
514a25cb 651 _error->FatalE("seteuid", "seteuid %u failed", old_euid);
226c0f64 652 if (setegid(old_egid) != 0)
514a25cb
DK
653 _error->FatalE("setegid", "setegid %u failed", old_egid);
654 if (setgroups(old_gidlist_nr, old_gidlist.get()))
655 _error->FatalE("setgroups", "setgroups %u failed", 0);
7c8206bf 656}
1c5f7e5f 657pkgAcquire::RunResult pkgAcquire::Run(int PulseIntervall)
0a8a80e5 658{
95278287 659 _error->PushToStack();
7c8206bf
DK
660 CheckDropPrivsMustBeDisabled(*this);
661
8b89e57f
AL
662 Running = true;
663
0a8a80e5
AL
664 for (Queue *I = Queues; I != 0; I = I->Next)
665 I->Startup();
666
b98f2859
AL
667 if (Log != 0)
668 Log->Start();
669
024d1123
AL
670 bool WasCancelled = false;
671
0a8a80e5 672 // Run till all things have been acquired
8267fe24
AL
673 struct timeval tv;
674 tv.tv_sec = 0;
1c5f7e5f 675 tv.tv_usec = PulseIntervall;
0a8a80e5
AL
676 while (ToFetch > 0)
677 {
678 fd_set RFds;
679 fd_set WFds;
680 int Highest = 0;
681 FD_ZERO(&RFds);
682 FD_ZERO(&WFds);
683 SetFds(Highest,&RFds,&WFds);
684
b0db36b1
AL
685 int Res;
686 do
687 {
688 Res = select(Highest+1,&RFds,&WFds,0,&tv);
689 }
690 while (Res < 0 && errno == EINTR);
691
8267fe24 692 if (Res < 0)
8b89e57f 693 {
8267fe24
AL
694 _error->Errno("select","Select has failed");
695 break;
8b89e57f 696 }
95278287 697
a416a90b
MV
698 if(RunFdsSane(&RFds,&WFds) == false)
699 break;
95278287 700
8267fe24
AL
701 // Timeout, notify the log class
702 if (Res == 0 || (Log != 0 && Log->Update == true))
703 {
1c5f7e5f 704 tv.tv_usec = PulseIntervall;
8267fe24
AL
705 for (Worker *I = Workers; I != 0; I = I->NextAcquire)
706 I->Pulse();
024d1123
AL
707 if (Log != 0 && Log->Pulse(this) == false)
708 {
709 WasCancelled = true;
710 break;
711 }
8267fe24 712 }
0a8a80e5 713 }
be4401bf 714
b98f2859
AL
715 if (Log != 0)
716 Log->Stop();
717
be4401bf
AL
718 // Shut down the acquire bits
719 Running = false;
0a8a80e5 720 for (Queue *I = Queues; I != 0; I = I->Next)
8e5fc8f5 721 I->Shutdown(false);
0a8a80e5 722
ab559b35 723 // Shut down the items
f7f0d6c7 724 for (ItemIterator I = Items.begin(); I != Items.end(); ++I)
95278287
DK
725 (*I)->Finished();
726
727 bool const newError = _error->PendingError();
728 _error->MergeWithStack();
729 if (newError)
024d1123
AL
730 return Failed;
731 if (WasCancelled)
732 return Cancelled;
733 return Continue;
93bf083d
AL
734}
735 /*}}}*/
be4401bf 736// Acquire::Bump - Called when an item is dequeued /*{{{*/
93bf083d
AL
737// ---------------------------------------------------------------------
738/* This routine bumps idle queues in hopes that they will be able to fetch
739 the dequeued item */
740void pkgAcquire::Bump()
741{
be4401bf
AL
742 for (Queue *I = Queues; I != 0; I = I->Next)
743 I->Bump();
0a8a80e5
AL
744}
745 /*}}}*/
8267fe24
AL
746// Acquire::WorkerStep - Step to the next worker /*{{{*/
747// ---------------------------------------------------------------------
748/* Not inlined to advoid including acquire-worker.h */
749pkgAcquire::Worker *pkgAcquire::WorkerStep(Worker *I)
750{
751 return I->NextAcquire;
d3e8fbb3 752}
8267fe24 753 /*}}}*/
a6568219 754// Acquire::Clean - Cleans a directory /*{{{*/
7a7fa5f0
AL
755// ---------------------------------------------------------------------
756/* This is a bit simplistic, it looks at every file in the dir and sees
757 if it is part of the download set. */
758bool pkgAcquire::Clean(string Dir)
759{
95b5f6c1
DK
760 // non-existing directories are by definition clean…
761 if (DirectoryExists(Dir) == false)
762 return true;
763
10ecfe4f
MV
764 if(Dir == "/")
765 return _error->Error(_("Clean of %s is not supported"), Dir.c_str());
766
7a7fa5f0
AL
767 DIR *D = opendir(Dir.c_str());
768 if (D == 0)
b2e465d6 769 return _error->Errno("opendir",_("Unable to read %s"),Dir.c_str());
7a7fa5f0
AL
770
771 string StartDir = SafeGetCWD();
772 if (chdir(Dir.c_str()) != 0)
773 {
774 closedir(D);
b2e465d6 775 return _error->Errno("chdir",_("Unable to change to %s"),Dir.c_str());
7a7fa5f0
AL
776 }
777
778 for (struct dirent *Dir = readdir(D); Dir != 0; Dir = readdir(D))
779 {
780 // Skip some files..
781 if (strcmp(Dir->d_name,"lock") == 0 ||
782 strcmp(Dir->d_name,"partial") == 0 ||
6aef1942 783 strcmp(Dir->d_name,"lost+found") == 0 ||
7a7fa5f0
AL
784 strcmp(Dir->d_name,".") == 0 ||
785 strcmp(Dir->d_name,"..") == 0)
786 continue;
787
788 // Look in the get list
b4fc9b6f 789 ItemCIterator I = Items.begin();
f7f0d6c7 790 for (; I != Items.end(); ++I)
7a7fa5f0
AL
791 if (flNotDir((*I)->DestFile) == Dir->d_name)
792 break;
793
794 // Nothing found, nuke it
795 if (I == Items.end())
ce1f3a2c 796 RemoveFile("Clean", Dir->d_name);
7a7fa5f0
AL
797 };
798
7a7fa5f0 799 closedir(D);
3c8cda8b
MV
800 if (chdir(StartDir.c_str()) != 0)
801 return _error->Errno("chdir",_("Unable to change to %s"),StartDir.c_str());
7a7fa5f0
AL
802 return true;
803}
804 /*}}}*/
a6568219
AL
805// Acquire::TotalNeeded - Number of bytes to fetch /*{{{*/
806// ---------------------------------------------------------------------
807/* This is the total number of bytes needed */
a02db58f 808APT_PURE unsigned long long pkgAcquire::TotalNeeded()
a6568219 809{
e551e123 810 return std::accumulate(ItemsBegin(), ItemsEnd(), 0llu,
ae732225
DK
811 [](unsigned long long const T, Item const * const I) {
812 return T + I->FileSize;
813 });
a6568219
AL
814}
815 /*}}}*/
816// Acquire::FetchNeeded - Number of bytes needed to get /*{{{*/
817// ---------------------------------------------------------------------
818/* This is the number of bytes that is not local */
a02db58f 819APT_PURE unsigned long long pkgAcquire::FetchNeeded()
a6568219 820{
5a978348 821 return std::accumulate(ItemsBegin(), ItemsEnd(), 0llu,
ae732225
DK
822 [](unsigned long long const T, Item const * const I) {
823 if (I->Local == false)
824 return T + I->FileSize;
825 else
826 return T;
827 });
a6568219
AL
828}
829 /*}}}*/
6b1ff003
AL
830// Acquire::PartialPresent - Number of partial bytes we already have /*{{{*/
831// ---------------------------------------------------------------------
832/* This is the number of bytes that is not local */
a02db58f 833APT_PURE unsigned long long pkgAcquire::PartialPresent()
6b1ff003 834{
5a978348 835 return std::accumulate(ItemsBegin(), ItemsEnd(), 0llu,
ae732225
DK
836 [](unsigned long long const T, Item const * const I) {
837 if (I->Local == false)
838 return T + I->PartialSize;
839 else
840 return T;
841 });
6b1ff003 842}
92fcbfc1 843 /*}}}*/
8e5fc8f5 844// Acquire::UriBegin - Start iterator for the uri list /*{{{*/
f7a08e33
AL
845// ---------------------------------------------------------------------
846/* */
847pkgAcquire::UriIterator pkgAcquire::UriBegin()
848{
849 return UriIterator(Queues);
850}
851 /*}}}*/
8e5fc8f5 852// Acquire::UriEnd - End iterator for the uri list /*{{{*/
f7a08e33
AL
853// ---------------------------------------------------------------------
854/* */
855pkgAcquire::UriIterator pkgAcquire::UriEnd()
856{
857 return UriIterator(0);
858}
859 /*}}}*/
e331f6ed
AL
860// Acquire::MethodConfig::MethodConfig - Constructor /*{{{*/
861// ---------------------------------------------------------------------
862/* */
25613a61
DK
863pkgAcquire::MethodConfig::MethodConfig() : d(NULL), Next(0), SingleInstance(false),
864 Pipeline(false), SendConfig(false), LocalOnly(false), NeedsCleanup(false),
865 Removable(false)
e331f6ed 866{
e331f6ed
AL
867}
868 /*}}}*/
0a8a80e5
AL
869// Queue::Queue - Constructor /*{{{*/
870// ---------------------------------------------------------------------
871/* */
e8afd168
DK
872pkgAcquire::Queue::Queue(string const &name,pkgAcquire * const owner) : d(NULL), Next(0),
873 Name(name), Items(0), Workers(0), Owner(owner), PipeDepth(0), MaxPipeDepth(1)
0a8a80e5 874{
0a8a80e5
AL
875}
876 /*}}}*/
877// Queue::~Queue - Destructor /*{{{*/
878// ---------------------------------------------------------------------
879/* */
880pkgAcquire::Queue::~Queue()
881{
8e5fc8f5 882 Shutdown(true);
0a8a80e5
AL
883
884 while (Items != 0)
885 {
886 QItem *Jnk = Items;
887 Items = Items->Next;
888 delete Jnk;
889 }
890}
891 /*}}}*/
892// Queue::Enqueue - Queue an item to the queue /*{{{*/
893// ---------------------------------------------------------------------
894/* */
c03462c6 895bool pkgAcquire::Queue::Enqueue(ItemDesc &Item)
0a8a80e5 896{
7a1b1f8b 897 QItem **I = &Items;
c03462c6
MV
898 // move to the end of the queue and check for duplicates here
899 for (; *I != 0; I = &(*I)->Next)
feb674ab 900 if (Item.URI == (*I)->URI)
c03462c6 901 {
9d2a8a73
DK
902 if (_config->FindB("Debug::pkgAcquire::Worker",false) == true)
903 std::cerr << " @ Queue: Action combined for " << Item.URI << " and " << (*I)->URI << std::endl;
08ea7806
DK
904 (*I)->Owners.push_back(Item.Owner);
905 Item.Owner->Status = (*I)->Owner->Status;
c03462c6
MV
906 return false;
907 }
908
0a8a80e5 909 // Create a new item
7a1b1f8b
AL
910 QItem *Itm = new QItem;
911 *Itm = Item;
912 Itm->Next = 0;
913 *I = Itm;
0a8a80e5 914
8267fe24 915 Item.Owner->QueueCounter++;
93bf083d
AL
916 if (Items->Next == 0)
917 Cycle();
c03462c6 918 return true;
0a8a80e5
AL
919}
920 /*}}}*/
c88edf1d 921// Queue::Dequeue - Remove an item from the queue /*{{{*/
0a8a80e5 922// ---------------------------------------------------------------------
b185acc2 923/* We return true if we hit something */
bfd22fc0 924bool pkgAcquire::Queue::Dequeue(Item *Owner)
0a8a80e5 925{
b185acc2
AL
926 if (Owner->Status == pkgAcquire::Item::StatFetching)
927 return _error->Error("Tried to dequeue a fetching object");
08ea7806 928
bfd22fc0 929 bool Res = false;
08ea7806 930
0a8a80e5
AL
931 QItem **I = &Items;
932 for (; *I != 0;)
933 {
08ea7806 934 if (Owner == (*I)->Owner)
0a8a80e5
AL
935 {
936 QItem *Jnk= *I;
937 *I = (*I)->Next;
938 Owner->QueueCounter--;
939 delete Jnk;
bfd22fc0 940 Res = true;
0a8a80e5
AL
941 }
942 else
943 I = &(*I)->Next;
944 }
08ea7806 945
bfd22fc0 946 return Res;
0a8a80e5
AL
947}
948 /*}}}*/
949// Queue::Startup - Start the worker processes /*{{{*/
950// ---------------------------------------------------------------------
8e5fc8f5
AL
951/* It is possible for this to be called with a pre-existing set of
952 workers. */
0a8a80e5
AL
953bool pkgAcquire::Queue::Startup()
954{
8e5fc8f5
AL
955 if (Workers == 0)
956 {
957 URI U(Name);
2e2865ae
DK
958 pkgAcquire::MethodConfig * const Cnf = Owner->GetConfig(U.Access);
959 if (unlikely(Cnf == nullptr))
8e5fc8f5 960 return false;
2e2865ae
DK
961
962 // now-running twin of the pkgAcquire::Enqueue call
963 for (QItem *I = Items; I != 0; )
964 {
10443549 965 auto const INext = I->Next;
2e2865ae 966 for (auto &&O: I->Owners)
10443549
DK
967 CheckForBadItemAndFailIt(O, Cnf, Owner->Log);
968 // if an item failed, it will be auto-dequeued invalidation our I here
969 I = INext;
2e2865ae
DK
970 }
971
8e5fc8f5
AL
972 Workers = new Worker(this,Cnf,Owner->Log);
973 Owner->Add(Workers);
974 if (Workers->Start() == false)
975 return false;
976
977 /* When pipelining we commit 10 items. This needs to change when we
978 added other source retry to have cycle maintain a pipeline depth
979 on its own. */
980 if (Cnf->Pipeline == true)
6ce72612 981 MaxPipeDepth = _config->FindI("Acquire::Max-Pipeline-Depth",10);
8e5fc8f5
AL
982 else
983 MaxPipeDepth = 1;
984 }
5cb5d8dc 985
93bf083d 986 return Cycle();
0a8a80e5
AL
987}
988 /*}}}*/
989// Queue::Shutdown - Shutdown the worker processes /*{{{*/
990// ---------------------------------------------------------------------
8e5fc8f5
AL
991/* If final is true then all workers are eliminated, otherwise only workers
992 that do not need cleanup are removed */
993bool pkgAcquire::Queue::Shutdown(bool Final)
0a8a80e5
AL
994{
995 // Delete all of the workers
8e5fc8f5
AL
996 pkgAcquire::Worker **Cur = &Workers;
997 while (*Cur != 0)
0a8a80e5 998 {
8e5fc8f5
AL
999 pkgAcquire::Worker *Jnk = *Cur;
1000 if (Final == true || Jnk->GetConf()->NeedsCleanup == false)
1001 {
1002 *Cur = Jnk->NextQueue;
1003 Owner->Remove(Jnk);
1004 delete Jnk;
1005 }
1006 else
1007 Cur = &(*Cur)->NextQueue;
0a8a80e5
AL
1008 }
1009
1010 return true;
3b5421b4
AL
1011}
1012 /*}}}*/
7d8afa39 1013// Queue::FindItem - Find a URI in the item list /*{{{*/
c88edf1d
AL
1014// ---------------------------------------------------------------------
1015/* */
1016pkgAcquire::Queue::QItem *pkgAcquire::Queue::FindItem(string URI,pkgAcquire::Worker *Owner)
1017{
1018 for (QItem *I = Items; I != 0; I = I->Next)
1019 if (I->URI == URI && I->Worker == Owner)
1020 return I;
1021 return 0;
1022}
1023 /*}}}*/
1024// Queue::ItemDone - Item has been completed /*{{{*/
1025// ---------------------------------------------------------------------
1026/* The worker signals this which causes the item to be removed from the
93bf083d
AL
1027 queue. If this is the last queue instance then it is removed from the
1028 main queue too.*/
c88edf1d
AL
1029bool pkgAcquire::Queue::ItemDone(QItem *Itm)
1030{
b185acc2 1031 PipeDepth--;
08ea7806
DK
1032 for (QItem::owner_iterator O = Itm->Owners.begin(); O != Itm->Owners.end(); ++O)
1033 {
1034 if ((*O)->Status == pkgAcquire::Item::StatFetching)
1035 (*O)->Status = pkgAcquire::Item::StatDone;
1036 }
1037
93bf083d
AL
1038 if (Itm->Owner->QueueCounter <= 1)
1039 Owner->Dequeue(Itm->Owner);
1040 else
1041 {
1042 Dequeue(Itm->Owner);
1043 Owner->Bump();
1044 }
08ea7806 1045
93bf083d
AL
1046 return Cycle();
1047}
1048 /*}}}*/
1049// Queue::Cycle - Queue new items into the method /*{{{*/
1050// ---------------------------------------------------------------------
b185acc2
AL
1051/* This locates a new idle item and sends it to the worker. If pipelining
1052 is enabled then it keeps the pipe full. */
93bf083d
AL
1053bool pkgAcquire::Queue::Cycle()
1054{
1055 if (Items == 0 || Workers == 0)
c88edf1d
AL
1056 return true;
1057
e7432370
AL
1058 if (PipeDepth < 0)
1059 return _error->Error("Pipedepth failure");
08ea7806 1060
93bf083d
AL
1061 // Look for a queable item
1062 QItem *I = Items;
e7432370 1063 while (PipeDepth < (signed)MaxPipeDepth)
b185acc2
AL
1064 {
1065 for (; I != 0; I = I->Next)
1066 if (I->Owner->Status == pkgAcquire::Item::StatIdle)
1067 break;
08ea7806 1068
b185acc2
AL
1069 // Nothing to do, queue is idle.
1070 if (I == 0)
1071 return true;
08ea7806 1072
b185acc2 1073 I->Worker = Workers;
ae732225
DK
1074 for (auto const &O: I->Owners)
1075 O->Status = pkgAcquire::Item::StatFetching;
e7432370 1076 PipeDepth++;
b185acc2
AL
1077 if (Workers->QueueItem(I) == false)
1078 return false;
1079 }
08ea7806 1080
b185acc2 1081 return true;
c88edf1d
AL
1082}
1083 /*}}}*/
be4401bf
AL
1084// Queue::Bump - Fetch any pending objects if we are idle /*{{{*/
1085// ---------------------------------------------------------------------
b185acc2 1086/* This is called when an item in multiple queues is dequeued */
be4401bf
AL
1087void pkgAcquire::Queue::Bump()
1088{
b185acc2 1089 Cycle();
be4401bf
AL
1090}
1091 /*}}}*/
08ea7806
DK
1092HashStringList pkgAcquire::Queue::QItem::GetExpectedHashes() const /*{{{*/
1093{
1094 /* each Item can have multiple owners and each owner might have different
1095 hashes, even if that is unlikely in practice and if so at least some
1096 owners will later fail. There is one situation through which is not a
1097 failure and still needs this handling: Two owners who expect the same
1098 file, but one owner only knows the SHA1 while the other only knows SHA256. */
1099 HashStringList superhsl;
1100 for (pkgAcquire::Queue::QItem::owner_iterator O = Owners.begin(); O != Owners.end(); ++O)
1101 {
1102 HashStringList const hsl = (*O)->GetExpectedHashes();
1103 if (hsl.usable() == false)
1104 continue;
1105 if (superhsl.usable() == false)
1106 superhsl = hsl;
1107 else
1108 {
1109 // we merge both lists - if we find disagreement send no hashes
1110 HashStringList::const_iterator hs = hsl.begin();
1111 for (; hs != hsl.end(); ++hs)
1112 if (superhsl.push_back(*hs) == false)
1113 break;
1114 if (hs != hsl.end())
1115 {
1116 superhsl.clear();
1117 break;
1118 }
1119 }
1120 }
1121 return superhsl;
1122}
1123 /*}}}*/
1124APT_PURE unsigned long long pkgAcquire::Queue::QItem::GetMaximumSize() const /*{{{*/
1125{
1126 unsigned long long Maximum = std::numeric_limits<unsigned long long>::max();
ae732225 1127 for (auto const &O: Owners)
08ea7806 1128 {
ae732225 1129 if (O->FileSize == 0)
08ea7806 1130 continue;
ae732225 1131 Maximum = std::min(Maximum, O->FileSize);
08ea7806
DK
1132 }
1133 if (Maximum == std::numeric_limits<unsigned long long>::max())
1134 return 0;
1135 return Maximum;
1136}
1137 /*}}}*/
1138void pkgAcquire::Queue::QItem::SyncDestinationFiles() const /*{{{*/
1139{
1140 /* ensure that the first owner has the best partial file of all and
1141 the rest have (potentially dangling) symlinks to it so that
1142 everything (like progress reporting) finds it easily */
1143 std::string superfile = Owner->DestFile;
1144 off_t supersize = 0;
1145 for (pkgAcquire::Queue::QItem::owner_iterator O = Owners.begin(); O != Owners.end(); ++O)
1146 {
1147 if ((*O)->DestFile == superfile)
1148 continue;
1149 struct stat file;
1150 if (lstat((*O)->DestFile.c_str(),&file) == 0)
1151 {
1152 if ((file.st_mode & S_IFREG) == 0)
ce1f3a2c 1153 RemoveFile("SyncDestinationFiles", (*O)->DestFile);
08ea7806
DK
1154 else if (supersize < file.st_size)
1155 {
1156 supersize = file.st_size;
ce1f3a2c 1157 RemoveFile("SyncDestinationFiles", superfile);
08ea7806
DK
1158 rename((*O)->DestFile.c_str(), superfile.c_str());
1159 }
1160 else
ce1f3a2c 1161 RemoveFile("SyncDestinationFiles", (*O)->DestFile);
08ea7806
DK
1162 if (symlink(superfile.c_str(), (*O)->DestFile.c_str()) != 0)
1163 {
1164 ; // not a problem per-se and no real alternative
1165 }
1166 }
1167 }
1168}
1169 /*}}}*/
1170std::string pkgAcquire::Queue::QItem::Custom600Headers() const /*{{{*/
1171{
1172 /* The others are relatively easy to merge, but this one?
1173 Lets not merge and see how far we can run with it…
1174 Likely, nobody will ever notice as all the items will
1175 be of the same class and hence generate the same headers. */
1176 return Owner->Custom600Headers();
1177}
1178 /*}}}*/
1179
b98f2859
AL
1180// AcquireStatus::pkgAcquireStatus - Constructor /*{{{*/
1181// ---------------------------------------------------------------------
1182/* */
533fe3d1 1183pkgAcquireStatus::pkgAcquireStatus() : d(NULL), Percent(-1), Update(true), MorePulses(false)
b98f2859
AL
1184{
1185 Start();
1186}
1187 /*}}}*/
1188// AcquireStatus::Pulse - Called periodically /*{{{*/
1189// ---------------------------------------------------------------------
1190/* This computes some internal state variables for the derived classes to
1191 use. It generates the current downloaded bytes and total bytes to download
1192 as well as the current CPS estimate. */
024d1123 1193bool pkgAcquireStatus::Pulse(pkgAcquire *Owner)
b98f2859
AL
1194{
1195 TotalBytes = 0;
1196 CurrentBytes = 0;
d568ed2d
AL
1197 TotalItems = 0;
1198 CurrentItems = 0;
fb193b1c 1199
b98f2859
AL
1200 // Compute the total number of bytes to fetch
1201 unsigned int Unknown = 0;
1202 unsigned int Count = 0;
1eba782f 1203 bool ExpectAdditionalItems = false;
c6e9cc58
MV
1204 for (pkgAcquire::ItemCIterator I = Owner->ItemsBegin();
1205 I != Owner->ItemsEnd();
f7f0d6c7 1206 ++I, ++Count)
b98f2859 1207 {
d568ed2d
AL
1208 TotalItems++;
1209 if ((*I)->Status == pkgAcquire::Item::StatDone)
f7f0d6c7 1210 ++CurrentItems;
b2e465d6 1211
1eba782f
DK
1212 // do we expect to acquire more files than we know of yet?
1213 if ((*I)->ExpectedAdditionalItems > 0)
1214 ExpectAdditionalItems = true;
c6e9cc58 1215
b98f2859
AL
1216 TotalBytes += (*I)->FileSize;
1217 if ((*I)->Complete == true)
1218 CurrentBytes += (*I)->FileSize;
1219 if ((*I)->FileSize == 0 && (*I)->Complete == false)
f7f0d6c7 1220 ++Unknown;
b98f2859 1221 }
fb193b1c 1222
b98f2859 1223 // Compute the current completion
dbbc5494 1224 unsigned long long ResumeSize = 0;
b98f2859
AL
1225 for (pkgAcquire::Worker *I = Owner->WorkersBegin(); I != 0;
1226 I = Owner->WorkerStep(I))
c62f7898 1227 {
b98f2859 1228 if (I->CurrentItem != 0 && I->CurrentItem->Owner->Complete == false)
aa0e1101
AL
1229 {
1230 CurrentBytes += I->CurrentSize;
1231 ResumeSize += I->ResumePoint;
08ea7806 1232
aa0e1101 1233 // Files with unknown size always have 100% completion
08ea7806 1234 if (I->CurrentItem->Owner->FileSize == 0 &&
aa0e1101
AL
1235 I->CurrentItem->Owner->Complete == false)
1236 TotalBytes += I->CurrentSize;
1237 }
c62f7898 1238 }
aa0e1101 1239
b98f2859
AL
1240 // Normalize the figures and account for unknown size downloads
1241 if (TotalBytes <= 0)
1242 TotalBytes = 1;
1243 if (Unknown == Count)
1244 TotalBytes = Unknown;
18ef0a78
AL
1245
1246 // Wha?! Is not supposed to happen.
1247 if (CurrentBytes > TotalBytes)
1248 CurrentBytes = TotalBytes;
96c6cab1 1249
b98f2859
AL
1250 // Compute the CPS
1251 struct timeval NewTime;
1252 gettimeofday(&NewTime,0);
2ec1674d 1253 if ((NewTime.tv_sec - Time.tv_sec == 6 && NewTime.tv_usec > Time.tv_usec) ||
b98f2859
AL
1254 NewTime.tv_sec - Time.tv_sec > 6)
1255 {
f17ac097
AL
1256 double Delta = NewTime.tv_sec - Time.tv_sec +
1257 (NewTime.tv_usec - Time.tv_usec)/1000000.0;
b98f2859 1258
b98f2859 1259 // Compute the CPS value
f17ac097 1260 if (Delta < 0.01)
e331f6ed
AL
1261 CurrentCPS = 0;
1262 else
aa0e1101
AL
1263 CurrentCPS = ((CurrentBytes - ResumeSize) - LastBytes)/Delta;
1264 LastBytes = CurrentBytes - ResumeSize;
dbbc5494 1265 ElapsedTime = (unsigned long long)Delta;
b98f2859
AL
1266 Time = NewTime;
1267 }
024d1123 1268
533fe3d1 1269 double const OldPercent = Percent;
c6e9cc58 1270 // calculate the percentage, if we have too little data assume 1%
1eba782f 1271 if (ExpectAdditionalItems)
96c6cab1 1272 Percent = 0;
533fe3d1 1273 else
96c6cab1 1274 // use both files and bytes because bytes can be unreliable
533fe3d1 1275 Percent = (0.8 * (CurrentBytes/float(TotalBytes)*100.0) +
96c6cab1 1276 0.2 * (CurrentItems/float(TotalItems)*100.0));
fb193b1c
MV
1277
1278 // debug
1279 if (_config->FindB("Debug::acquire::progress", false) == true)
1280 {
1281 std::clog
1282 << "["
1283 << std::setw(5) << std::setprecision(4) << std::showpoint << Percent
1284 << "]"
1285 << " Bytes: "
1286 << SizeToStr(CurrentBytes) << " / " << SizeToStr(TotalBytes)
1287 << " # Files: "
1288 << CurrentItems << " / " << TotalItems
1289 << std::endl;
1290 }
1291
533fe3d1
DK
1292 double const DiffPercent = Percent - OldPercent;
1293 if (DiffPercent < 0.001 && _config->FindB("Acquire::Progress::Diffpercent", false) == true)
1294 return true;
96c6cab1 1295
75ef8f14
MV
1296 int fd = _config->FindI("APT::Status-Fd",-1);
1297 if(fd > 0)
1298 {
1299 ostringstream status;
1300
1301 char msg[200];
1302 long i = CurrentItems < TotalItems ? CurrentItems + 1 : CurrentItems;
c033d415
MV
1303 unsigned long long ETA = 0;
1304 if(CurrentCPS > 0)
1305 ETA = (TotalBytes - CurrentBytes) / CurrentCPS;
75ef8f14 1306
1e8b4c0f
MV
1307 // only show the ETA if it makes sense
1308 if (ETA > 0 && ETA < 172800 /* two days */ )
0c508b03 1309 snprintf(msg,sizeof(msg), _("Retrieving file %li of %li (%s remaining)"), i, TotalItems, TimeToStr(ETA).c_str());
1e8b4c0f 1310 else
0c508b03 1311 snprintf(msg,sizeof(msg), _("Retrieving file %li of %li"), i, TotalItems);
533fe3d1 1312
75ef8f14 1313 // build the status str
0919f1df
DK
1314 std::ostringstream str;
1315 str.imbue(std::locale("C.UTF-8"));
1316 str.precision(4);
1317 str << "dlstatus" << ':' << std::fixed << i << ':' << Percent << ':' << msg << '\n';
1318 auto const dlstatus = str.str();
b58e2c7c 1319 FileFd::Write(fd, dlstatus.data(), dlstatus.size());
75ef8f14
MV
1320 }
1321
024d1123 1322 return true;
b98f2859
AL
1323}
1324 /*}}}*/
1325// AcquireStatus::Start - Called when the download is started /*{{{*/
1326// ---------------------------------------------------------------------
1327/* We just reset the counters */
1328void pkgAcquireStatus::Start()
1329{
1330 gettimeofday(&Time,0);
1331 gettimeofday(&StartTime,0);
1332 LastBytes = 0;
1333 CurrentCPS = 0;
1334 CurrentBytes = 0;
1335 TotalBytes = 0;
1336 FetchedBytes = 0;
1337 ElapsedTime = 0;
d568ed2d
AL
1338 TotalItems = 0;
1339 CurrentItems = 0;
b98f2859
AL
1340}
1341 /*}}}*/
a6568219 1342// AcquireStatus::Stop - Finished downloading /*{{{*/
b98f2859
AL
1343// ---------------------------------------------------------------------
1344/* This accurately computes the elapsed time and the total overall CPS. */
1345void pkgAcquireStatus::Stop()
1346{
1347 // Compute the CPS and elapsed time
1348 struct timeval NewTime;
1349 gettimeofday(&NewTime,0);
1350
31a0531d
AL
1351 double Delta = NewTime.tv_sec - StartTime.tv_sec +
1352 (NewTime.tv_usec - StartTime.tv_usec)/1000000.0;
b98f2859 1353
b98f2859 1354 // Compute the CPS value
31a0531d 1355 if (Delta < 0.01)
e331f6ed
AL
1356 CurrentCPS = 0;
1357 else
31a0531d 1358 CurrentCPS = FetchedBytes/Delta;
b98f2859 1359 LastBytes = CurrentBytes;
dbbc5494 1360 ElapsedTime = (unsigned long long)Delta;
b98f2859
AL
1361}
1362 /*}}}*/
1363// AcquireStatus::Fetched - Called when a byte set has been fetched /*{{{*/
1364// ---------------------------------------------------------------------
1365/* This is used to get accurate final transfer rate reporting. */
73da43e9 1366void pkgAcquireStatus::Fetched(unsigned long long Size,unsigned long long Resume)
93274b8d 1367{
b98f2859
AL
1368 FetchedBytes += Size - Resume;
1369}
1370 /*}}}*/
862bafea 1371
c8a4ce6c
DK
1372pkgAcquire::UriIterator::UriIterator(pkgAcquire::Queue *Q) : d(NULL), CurQ(Q), CurItem(0)
1373{
1374 while (CurItem == 0 && CurQ != 0)
1375 {
1376 CurItem = CurQ->Items;
1377 CurQ = CurQ->Next;
1378 }
1379}
1380
9d653a6d
DK
1381APT_CONST pkgAcquire::UriIterator::~UriIterator() {}
1382APT_CONST pkgAcquire::MethodConfig::~MethodConfig() {}
1383APT_CONST pkgAcquireStatus::~pkgAcquireStatus() {}