233247ae3e368933ae6e9d0349e46fb5c00eb6c1
[apt.git] / apt-pkg / acquire.cc
1 // -*- mode: cpp; mode: fold -*-
2 // Description                                                          /*{{{*/
3 // $Id: acquire.cc,v 1.2 2002/07/25 18:07:18 niemeyer Exp $
4 /* ######################################################################
5
6    Acquire - File Acquiration
7
8    The core element for the schedual system is the concept of a named
9    queue. Each queue is unique and each queue has a name derived from the
10    URI. The degree of paralization can be controled by how the queue
11    name is derived from the URI.
12    
13    ##################################################################### */
14                                                                         /*}}}*/
15 // Include Files                                                        /*{{{*/
16 #ifdef __GNUG__
17 #pragma implementation "apt-pkg/acquire.h"
18 #endif       
19 #include <apt-pkg/acquire.h>
20 #include <apt-pkg/acquire-item.h>
21 #include <apt-pkg/acquire-worker.h>
22 #include <apt-pkg/configuration.h>
23 #include <apt-pkg/error.h>
24 #include <apt-pkg/strutl.h>
25
26 #include <apti18n.h>
27
28 #include <iostream>
29     
30 #include <dirent.h>
31 #include <sys/time.h>
32 #include <errno.h>
33 #include <sys/stat.h>
34                                                                         /*}}}*/
35
36 using namespace std;
37
38 // Acquire::pkgAcquire - Constructor                                    /*{{{*/
39 // ---------------------------------------------------------------------
40 /* We grab some runtime state from the configuration space */
41 pkgAcquire::pkgAcquire(pkgAcquireStatus *Log) : Log(Log)
42 {
43    Queues = 0;
44    Configs = 0;
45    Workers = 0;
46    ToFetch = 0;
47    Running = false;
48    
49    string Mode = _config->Find("Acquire::Queue-Mode","host");
50    if (strcasecmp(Mode.c_str(),"host") == 0)
51       QueueMode = QueueHost;
52    if (strcasecmp(Mode.c_str(),"access") == 0)
53       QueueMode = QueueAccess;   
54
55    Debug = _config->FindB("Debug::pkgAcquire",false);
56    
57    // This is really a stupid place for this
58    struct stat St;
59    if (stat((_config->FindDir("Dir::State::lists") + "partial/").c_str(),&St) != 0 ||
60        S_ISDIR(St.st_mode) == 0)
61       _error->Error(_("Lists directory %spartial is missing."),
62                     _config->FindDir("Dir::State::lists").c_str());
63    if (stat((_config->FindDir("Dir::Cache::Archives") + "partial/").c_str(),&St) != 0 ||
64        S_ISDIR(St.st_mode) == 0)
65       _error->Error(_("Archive directory %spartial is missing."),
66                     _config->FindDir("Dir::Cache::Archives").c_str());
67 }
68                                                                         /*}}}*/
69 // Acquire::~pkgAcquire - Destructor                                    /*{{{*/
70 // ---------------------------------------------------------------------
71 /* Free our memory, clean up the queues (destroy the workers) */
72 pkgAcquire::~pkgAcquire()
73 {
74    Shutdown();
75    
76    while (Configs != 0)
77    {
78       MethodConfig *Jnk = Configs;
79       Configs = Configs->Next;
80       delete Jnk;
81    }   
82 }
83                                                                         /*}}}*/
84 // Acquire::Shutdown - Clean out the acquire object                     /*{{{*/
85 // ---------------------------------------------------------------------
86 /* */
87 void pkgAcquire::Shutdown()
88 {
89    while (Items.size() != 0)
90       delete Items[0];
91
92    while (Queues != 0)
93    {
94       Queue *Jnk = Queues;
95       Queues = Queues->Next;
96       delete Jnk;
97    }   
98 }
99                                                                         /*}}}*/
100 // Acquire::Add - Add a new item                                        /*{{{*/
101 // ---------------------------------------------------------------------
102 /* This puts an item on the acquire list. This list is mainly for tracking
103    item status */
104 void pkgAcquire::Add(Item *Itm)
105 {
106    Items.push_back(Itm);
107 }
108                                                                         /*}}}*/
109 // Acquire::Remove - Remove a item                                      /*{{{*/
110 // ---------------------------------------------------------------------
111 /* Remove an item from the acquire list. This is usually not used.. */
112 void pkgAcquire::Remove(Item *Itm)
113 {
114    Dequeue(Itm);
115    
116    for (ItemIterator I = Items.begin(); I != Items.end();)
117    {
118       if (*I == Itm)
119       {
120          Items.erase(I);
121          I = Items.begin();
122       }      
123       else 
124          I++;
125    }
126 }
127                                                                         /*}}}*/
128 // Acquire::Add - Add a worker                                          /*{{{*/
129 // ---------------------------------------------------------------------
130 /* A list of workers is kept so that the select loop can direct their FD
131    usage. */
132 void pkgAcquire::Add(Worker *Work)
133 {
134    Work->NextAcquire = Workers;
135    Workers = Work;
136 }
137                                                                         /*}}}*/
138 // Acquire::Remove - Remove a worker                                    /*{{{*/
139 // ---------------------------------------------------------------------
140 /* A worker has died. This can not be done while the select loop is running
141    as it would require that RunFds could handling a changing list state and
142    it cant.. */
143 void pkgAcquire::Remove(Worker *Work)
144 {
145    if (Running == true)
146       abort();
147    
148    Worker **I = &Workers;
149    for (; *I != 0;)
150    {
151       if (*I == Work)
152          *I = (*I)->NextAcquire;
153       else
154          I = &(*I)->NextAcquire;
155    }
156 }
157                                                                         /*}}}*/
158 // Acquire::Enqueue - Queue an URI for fetching                         /*{{{*/
159 // ---------------------------------------------------------------------
160 /* This is the entry point for an item. An item calls this function when
161    it is constructed which creates a queue (based on the current queue
162    mode) and puts the item in that queue. If the system is running then
163    the queue might be started. */
164 void pkgAcquire::Enqueue(ItemDesc &Item)
165 {
166    // Determine which queue to put the item in
167    const MethodConfig *Config;
168    string Name = QueueName(Item.URI,Config);
169    if (Name.empty() == true)
170       return;
171
172    // Find the queue structure
173    Queue *I = Queues;
174    for (; I != 0 && I->Name != Name; I = I->Next);
175    if (I == 0)
176    {
177       I = new Queue(Name,this);
178       I->Next = Queues;
179       Queues = I;
180       
181       if (Running == true)
182          I->Startup();
183    }
184
185    // See if this is a local only URI
186    if (Config->LocalOnly == true && Item.Owner->Complete == false)
187       Item.Owner->Local = true;
188    Item.Owner->Status = Item::StatIdle;
189    
190    // Queue it into the named queue
191    I->Enqueue(Item);
192    ToFetch++;
193          
194    // Some trace stuff
195    if (Debug == true)
196    {
197       clog << "Fetching " << Item.URI << endl;
198       clog << " to " << Item.Owner->DestFile << endl;
199       clog << " Queue is: " << Name << endl;
200    }
201 }
202                                                                         /*}}}*/
203 // Acquire::Dequeue - Remove an item from all queues                    /*{{{*/
204 // ---------------------------------------------------------------------
205 /* This is called when an item is finished being fetched. It removes it
206    from all the queues */
207 void pkgAcquire::Dequeue(Item *Itm)
208 {
209    Queue *I = Queues;
210    bool Res = false;
211    for (; I != 0; I = I->Next)
212       Res |= I->Dequeue(Itm);
213    
214    if (Debug == true)
215       clog << "Dequeuing " << Itm->DestFile << endl;
216    if (Res == true)
217       ToFetch--;
218 }
219                                                                         /*}}}*/
220 // Acquire::QueueName - Return the name of the queue for this URI       /*{{{*/
221 // ---------------------------------------------------------------------
222 /* The string returned depends on the configuration settings and the
223    method parameters. Given something like http://foo.org/bar it can
224    return http://foo.org or http */
225 string pkgAcquire::QueueName(string Uri,MethodConfig const *&Config)
226 {
227    URI U(Uri);
228    
229    Config = GetConfig(U.Access);
230    if (Config == 0)
231       return string();
232    
233    /* Single-Instance methods get exactly one queue per URI. This is
234       also used for the Access queue method  */
235    if (Config->SingleInstance == true || QueueMode == QueueAccess)
236        return U.Access;
237
238    return U.Access + ':' + U.Host;
239 }
240                                                                         /*}}}*/
241 // Acquire::GetConfig - Fetch the configuration information             /*{{{*/
242 // ---------------------------------------------------------------------
243 /* This locates the configuration structure for an access method. If 
244    a config structure cannot be found a Worker will be created to
245    retrieve it */
246 pkgAcquire::MethodConfig *pkgAcquire::GetConfig(string Access)
247 {
248    // Search for an existing config
249    MethodConfig *Conf;
250    for (Conf = Configs; Conf != 0; Conf = Conf->Next)
251       if (Conf->Access == Access)
252          return Conf;
253    
254    // Create the new config class
255    Conf = new MethodConfig;
256    Conf->Access = Access;
257    Conf->Next = Configs;
258    Configs = Conf;
259
260    // Create the worker to fetch the configuration
261    Worker Work(Conf);
262    if (Work.Start() == false)
263       return 0;
264    
265    return Conf;
266 }
267                                                                         /*}}}*/
268 // Acquire::SetFds - Deal with readable FDs                             /*{{{*/
269 // ---------------------------------------------------------------------
270 /* Collect FDs that have activity monitors into the fd sets */
271 void pkgAcquire::SetFds(int &Fd,fd_set *RSet,fd_set *WSet)
272 {
273    for (Worker *I = Workers; I != 0; I = I->NextAcquire)
274    {
275       if (I->InReady == true && I->InFd >= 0)
276       {
277          if (Fd < I->InFd)
278             Fd = I->InFd;
279          FD_SET(I->InFd,RSet);
280       }
281       if (I->OutReady == true && I->OutFd >= 0)
282       {
283          if (Fd < I->OutFd)
284             Fd = I->OutFd;
285          FD_SET(I->OutFd,WSet);
286       }
287    }
288 }
289                                                                         /*}}}*/
290 // Acquire::RunFds - Deal with active FDs                               /*{{{*/
291 // ---------------------------------------------------------------------
292 /* Dispatch active FDs over to the proper workers. It is very important
293    that a worker never be erased while this is running! The queue class
294    should never erase a worker except during shutdown processing. */
295 void pkgAcquire::RunFds(fd_set *RSet,fd_set *WSet)
296 {
297    for (Worker *I = Workers; I != 0; I = I->NextAcquire)
298    {
299       if (I->InFd >= 0 && FD_ISSET(I->InFd,RSet) != 0)
300          I->InFdReady();
301       if (I->OutFd >= 0 && FD_ISSET(I->OutFd,WSet) != 0)
302          I->OutFdReady();
303    }
304 }
305                                                                         /*}}}*/
306 // Acquire::Run - Run the fetch sequence                                /*{{{*/
307 // ---------------------------------------------------------------------
308 /* This runs the queues. It manages a select loop for all of the
309    Worker tasks. The workers interact with the queues and items to
310    manage the actual fetch. */
311 pkgAcquire::RunResult pkgAcquire::Run()
312 {
313    Running = true;
314    
315    for (Queue *I = Queues; I != 0; I = I->Next)
316       I->Startup();
317    
318    if (Log != 0)
319       Log->Start();
320    
321    bool WasCancelled = false;
322
323    // Run till all things have been acquired
324    struct timeval tv;
325    tv.tv_sec = 0;
326    tv.tv_usec = 500000; 
327    while (ToFetch > 0)
328    {
329       fd_set RFds;
330       fd_set WFds;
331       int Highest = 0;
332       FD_ZERO(&RFds);
333       FD_ZERO(&WFds);
334       SetFds(Highest,&RFds,&WFds);
335       
336       int Res;
337       do
338       {
339          Res = select(Highest+1,&RFds,&WFds,0,&tv);
340       }
341       while (Res < 0 && errno == EINTR);
342       
343       if (Res < 0)
344       {
345          _error->Errno("select","Select has failed");
346          break;
347       }
348              
349       RunFds(&RFds,&WFds);
350       if (_error->PendingError() == true)
351          break;
352       
353       // Timeout, notify the log class
354       if (Res == 0 || (Log != 0 && Log->Update == true))
355       {
356          tv.tv_usec = 500000;
357          for (Worker *I = Workers; I != 0; I = I->NextAcquire)
358             I->Pulse();
359          if (Log != 0 && Log->Pulse(this) == false)
360          {
361             WasCancelled = true;
362             break;
363          }
364       }      
365    }   
366
367    if (Log != 0)
368       Log->Stop();
369    
370    // Shut down the acquire bits
371    Running = false;
372    for (Queue *I = Queues; I != 0; I = I->Next)
373       I->Shutdown(false);
374
375    // Shut down the items
376    for (ItemIterator I = Items.begin(); I != Items.end(); I++)
377       (*I)->Finished(); 
378    
379    if (_error->PendingError())
380       return Failed;
381    if (WasCancelled)
382       return Cancelled;
383    return Continue;
384 }
385                                                                         /*}}}*/
386 // Acquire::Bump - Called when an item is dequeued                      /*{{{*/
387 // ---------------------------------------------------------------------
388 /* This routine bumps idle queues in hopes that they will be able to fetch
389    the dequeued item */
390 void pkgAcquire::Bump()
391 {
392    for (Queue *I = Queues; I != 0; I = I->Next)
393       I->Bump();
394 }
395                                                                         /*}}}*/
396 // Acquire::WorkerStep - Step to the next worker                        /*{{{*/
397 // ---------------------------------------------------------------------
398 /* Not inlined to advoid including acquire-worker.h */
399 pkgAcquire::Worker *pkgAcquire::WorkerStep(Worker *I)
400 {
401    return I->NextAcquire;
402 };
403                                                                         /*}}}*/
404 // Acquire::Clean - Cleans a directory                                  /*{{{*/
405 // ---------------------------------------------------------------------
406 /* This is a bit simplistic, it looks at every file in the dir and sees
407    if it is part of the download set. */
408 bool pkgAcquire::Clean(string Dir)
409 {
410    DIR *D = opendir(Dir.c_str());   
411    if (D == 0)
412       return _error->Errno("opendir",_("Unable to read %s"),Dir.c_str());
413    
414    string StartDir = SafeGetCWD();
415    if (chdir(Dir.c_str()) != 0)
416    {
417       closedir(D);
418       return _error->Errno("chdir",_("Unable to change to %s"),Dir.c_str());
419    }
420    
421    for (struct dirent *Dir = readdir(D); Dir != 0; Dir = readdir(D))
422    {
423       // Skip some files..
424       if (strcmp(Dir->d_name,"lock") == 0 ||
425           strcmp(Dir->d_name,"partial") == 0 ||
426           strcmp(Dir->d_name,".") == 0 ||
427           strcmp(Dir->d_name,"..") == 0)
428          continue;
429       
430       // Look in the get list
431       ItemCIterator I = Items.begin();
432       for (; I != Items.end(); I++)
433          if (flNotDir((*I)->DestFile) == Dir->d_name)
434             break;
435       
436       // Nothing found, nuke it
437       if (I == Items.end())
438          unlink(Dir->d_name);
439    };
440    
441    chdir(StartDir.c_str());
442    closedir(D);
443    return true;   
444 }
445                                                                         /*}}}*/
446 // Acquire::TotalNeeded - Number of bytes to fetch                      /*{{{*/
447 // ---------------------------------------------------------------------
448 /* This is the total number of bytes needed */
449 double pkgAcquire::TotalNeeded()
450 {
451    double Total = 0;
452    for (ItemCIterator I = ItemsBegin(); I != ItemsEnd(); I++)
453       Total += (*I)->FileSize;
454    return Total;
455 }
456                                                                         /*}}}*/
457 // Acquire::FetchNeeded - Number of bytes needed to get                 /*{{{*/
458 // ---------------------------------------------------------------------
459 /* This is the number of bytes that is not local */
460 double pkgAcquire::FetchNeeded()
461 {
462    double Total = 0;
463    for (ItemCIterator I = ItemsBegin(); I != ItemsEnd(); I++)
464       if ((*I)->Local == false)
465          Total += (*I)->FileSize;
466    return Total;
467 }
468                                                                         /*}}}*/
469 // Acquire::PartialPresent - Number of partial bytes we already have    /*{{{*/
470 // ---------------------------------------------------------------------
471 /* This is the number of bytes that is not local */
472 double pkgAcquire::PartialPresent()
473 {
474   double Total = 0;
475    for (ItemCIterator I = ItemsBegin(); I != ItemsEnd(); I++)
476       if ((*I)->Local == false)
477          Total += (*I)->PartialSize;
478    return Total;
479 }
480                                                                         /*}}}*/
481 // Acquire::UriBegin - Start iterator for the uri list                  /*{{{*/
482 // ---------------------------------------------------------------------
483 /* */
484 pkgAcquire::UriIterator pkgAcquire::UriBegin()
485 {
486    return UriIterator(Queues);
487 }
488                                                                         /*}}}*/
489 // Acquire::UriEnd - End iterator for the uri list                      /*{{{*/
490 // ---------------------------------------------------------------------
491 /* */
492 pkgAcquire::UriIterator pkgAcquire::UriEnd()
493 {
494    return UriIterator(0);
495 }
496                                                                         /*}}}*/
497
498 // Acquire::MethodConfig::MethodConfig - Constructor                    /*{{{*/
499 // ---------------------------------------------------------------------
500 /* */
501 pkgAcquire::MethodConfig::MethodConfig()
502 {
503    SingleInstance = false;
504    Pipeline = false;
505    SendConfig = false;
506    LocalOnly = false;
507    Removable = false;
508    Next = 0;
509    // CNC:2004-04-27
510    HasPreferredURI = false;
511    DonePreferredURI = false;
512 }
513                                                                         /*}}}*/
514
515 // Queue::Queue - Constructor                                           /*{{{*/
516 // ---------------------------------------------------------------------
517 /* */
518 pkgAcquire::Queue::Queue(string Name,pkgAcquire *Owner) : Name(Name), 
519             Owner(Owner)
520 {
521    Items = 0;
522    Next = 0;
523    Workers = 0;
524    MaxPipeDepth = 1;
525    PipeDepth = 0;
526 }
527                                                                         /*}}}*/
528 // Queue::~Queue - Destructor                                           /*{{{*/
529 // ---------------------------------------------------------------------
530 /* */
531 pkgAcquire::Queue::~Queue()
532 {
533    Shutdown(true);
534    
535    while (Items != 0)
536    {
537       QItem *Jnk = Items;
538       Items = Items->Next;
539       delete Jnk;
540    }
541 }
542                                                                         /*}}}*/
543 // Queue::Enqueue - Queue an item to the queue                          /*{{{*/
544 // ---------------------------------------------------------------------
545 /* */
546 void pkgAcquire::Queue::Enqueue(ItemDesc &Item)
547 {
548    QItem **I = &Items;
549    for (; *I != 0; I = &(*I)->Next);
550    
551    // Create a new item
552    QItem *Itm = new QItem;
553    *Itm = Item;
554    Itm->Next = 0;
555    *I = Itm;
556    
557    Item.Owner->QueueCounter++;   
558    if (Items->Next == 0)
559       Cycle();
560 }
561                                                                         /*}}}*/
562 // Queue::Dequeue - Remove an item from the queue                       /*{{{*/
563 // ---------------------------------------------------------------------
564 /* We return true if we hit something */
565 bool pkgAcquire::Queue::Dequeue(Item *Owner)
566 {
567    if (Owner->Status == pkgAcquire::Item::StatFetching)
568       return _error->Error("Tried to dequeue a fetching object");
569        
570    bool Res = false;
571    
572    QItem **I = &Items;
573    for (; *I != 0;)
574    {
575       if ((*I)->Owner == Owner)
576       {
577          QItem *Jnk= *I;
578          *I = (*I)->Next;
579          Owner->QueueCounter--;
580          delete Jnk;
581          Res = true;
582       }
583       else
584          I = &(*I)->Next;
585    }
586    
587    return Res;
588 }
589                                                                         /*}}}*/
590 // Queue::Startup - Start the worker processes                          /*{{{*/
591 // ---------------------------------------------------------------------
592 /* It is possible for this to be called with a pre-existing set of
593    workers. */
594 bool pkgAcquire::Queue::Startup()
595 {
596    if (Workers == 0)
597    {
598       URI U(Name);
599       pkgAcquire::MethodConfig *Cnf = Owner->GetConfig(U.Access);
600       if (Cnf == 0)
601          return false;
602       
603       Workers = new Worker(this,Cnf,Owner->Log);
604       Owner->Add(Workers);
605       if (Workers->Start() == false)
606          return false;
607       
608       /* When pipelining we commit 10 items. This needs to change when we
609          added other source retry to have cycle maintain a pipeline depth
610          on its own. */
611       if (Cnf->Pipeline == true)
612          MaxPipeDepth = 10;
613       else
614          MaxPipeDepth = 1;
615    }
616    
617    return Cycle();
618 }
619                                                                         /*}}}*/
620 // Queue::Shutdown - Shutdown the worker processes                      /*{{{*/
621 // ---------------------------------------------------------------------
622 /* If final is true then all workers are eliminated, otherwise only workers
623    that do not need cleanup are removed */
624 bool pkgAcquire::Queue::Shutdown(bool Final)
625 {
626    // Delete all of the workers
627    pkgAcquire::Worker **Cur = &Workers;
628    while (*Cur != 0)
629    {
630       pkgAcquire::Worker *Jnk = *Cur;
631       if (Final == true || Jnk->GetConf()->NeedsCleanup == false)
632       {
633          *Cur = Jnk->NextQueue;
634          Owner->Remove(Jnk);
635          delete Jnk;
636       }
637       else
638          Cur = &(*Cur)->NextQueue;      
639    }
640    
641    return true;
642 }
643                                                                         /*}}}*/
644 // Queue::FindItem - Find a URI in the item list                        /*{{{*/
645 // ---------------------------------------------------------------------
646 /* */
647 pkgAcquire::Queue::QItem *pkgAcquire::Queue::FindItem(string URI,pkgAcquire::Worker *Owner)
648 {
649    for (QItem *I = Items; I != 0; I = I->Next)
650       if (I->URI == URI && I->Worker == Owner)
651          return I;
652    return 0;
653 }
654                                                                         /*}}}*/
655 // Queue::ItemDone - Item has been completed                            /*{{{*/
656 // ---------------------------------------------------------------------
657 /* The worker signals this which causes the item to be removed from the
658    queue. If this is the last queue instance then it is removed from the
659    main queue too.*/
660 bool pkgAcquire::Queue::ItemDone(QItem *Itm)
661 {
662    PipeDepth--;
663    if (Itm->Owner->Status == pkgAcquire::Item::StatFetching)
664       Itm->Owner->Status = pkgAcquire::Item::StatDone;
665    
666    if (Itm->Owner->QueueCounter <= 1)
667       Owner->Dequeue(Itm->Owner);
668    else
669    {
670       Dequeue(Itm->Owner);
671       Owner->Bump();
672    }
673    
674    return Cycle();
675 }
676                                                                         /*}}}*/
677 // Queue::Cycle - Queue new items into the method                       /*{{{*/
678 // ---------------------------------------------------------------------
679 /* This locates a new idle item and sends it to the worker. If pipelining
680    is enabled then it keeps the pipe full. */
681 bool pkgAcquire::Queue::Cycle()
682 {
683    if (Items == 0 || Workers == 0)
684       return true;
685
686    if (PipeDepth < 0)
687       return _error->Error("Pipedepth failure");
688                            
689    // Look for a queable item
690    // CNC:2004-04-27
691    bool Preferred = (Workers->Config->HasPreferredURI == true &&
692                      Workers->Config->DonePreferredURI == false &&
693                      Workers->Config->PreferredURI.empty() == false);
694    QItem *I = Items;
695    while (PipeDepth < (signed)MaxPipeDepth)
696    {
697       // CNC:2004-04-27
698       if (Preferred) {
699          for (; I != 0; I = I->Next)
700             if (I->Owner->Status == pkgAcquire::Item::StatIdle &&
701                 strncmp(I->URI.c_str(), Workers->Config->PreferredURI.c_str(),
702                         Workers->Config->PreferredURI.length()) == 0)
703                break;
704       } else {
705          for (; I != 0; I = I->Next)
706             if (I->Owner->Status == pkgAcquire::Item::StatIdle)
707                break;
708       }
709       
710       // Nothing to do, queue is idle.
711       if (I == 0) {
712          // CNC:2004-04-27
713          if (Preferred == true) {
714             Preferred = false;
715             Workers->Config->DonePreferredURI = true;
716             I = Items;
717             continue;
718          }
719          return true;
720       }
721       
722       I->Worker = Workers;
723       I->Owner->Status = pkgAcquire::Item::StatFetching;
724       PipeDepth++;
725       if (Workers->QueueItem(I) == false)
726          return false;
727    }
728    
729    return true;
730 }
731                                                                         /*}}}*/
732 // Queue::Bump - Fetch any pending objects if we are idle               /*{{{*/
733 // ---------------------------------------------------------------------
734 /* This is called when an item in multiple queues is dequeued */
735 void pkgAcquire::Queue::Bump()
736 {
737    Cycle();
738 }
739                                                                         /*}}}*/
740
741 // AcquireStatus::pkgAcquireStatus - Constructor                        /*{{{*/
742 // ---------------------------------------------------------------------
743 /* */
744 pkgAcquireStatus::pkgAcquireStatus() : Update(true), MorePulses(false)
745 {
746    Start();
747 }
748                                                                         /*}}}*/
749 // AcquireStatus::Pulse - Called periodically                           /*{{{*/
750 // ---------------------------------------------------------------------
751 /* This computes some internal state variables for the derived classes to
752    use. It generates the current downloaded bytes and total bytes to download
753    as well as the current CPS estimate. */
754 bool pkgAcquireStatus::Pulse(pkgAcquire *Owner)
755 {
756    TotalBytes = 0;
757    CurrentBytes = 0;
758    TotalItems = 0;
759    CurrentItems = 0;
760    
761    // Compute the total number of bytes to fetch
762    unsigned int Unknown = 0;
763    unsigned int Count = 0;
764    for (pkgAcquire::ItemCIterator I = Owner->ItemsBegin(); I != Owner->ItemsEnd();
765         I++, Count++)
766    {
767       TotalItems++;
768       if ((*I)->Status == pkgAcquire::Item::StatDone)
769          CurrentItems++;
770       
771       // Totally ignore local items
772       if ((*I)->Local == true)
773          continue;
774
775       TotalBytes += (*I)->FileSize;
776       if ((*I)->Complete == true)
777          CurrentBytes += (*I)->FileSize;
778       if ((*I)->FileSize == 0 && (*I)->Complete == false)
779          Unknown++;
780    }
781    
782    // Compute the current completion
783    unsigned long ResumeSize = 0;
784    for (pkgAcquire::Worker *I = Owner->WorkersBegin(); I != 0;
785         I = Owner->WorkerStep(I))
786       if (I->CurrentItem != 0 && I->CurrentItem->Owner->Complete == false)
787       {
788          CurrentBytes += I->CurrentSize;
789          ResumeSize += I->ResumePoint;
790          
791          // Files with unknown size always have 100% completion
792          if (I->CurrentItem->Owner->FileSize == 0 && 
793              I->CurrentItem->Owner->Complete == false)
794             TotalBytes += I->CurrentSize;
795       }
796    
797    // Normalize the figures and account for unknown size downloads
798    if (TotalBytes <= 0)
799       TotalBytes = 1;
800    if (Unknown == Count)
801       TotalBytes = Unknown;
802
803    // Wha?! Is not supposed to happen.
804    if (CurrentBytes > TotalBytes)
805       CurrentBytes = TotalBytes;
806    
807    // Compute the CPS
808    struct timeval NewTime;
809    gettimeofday(&NewTime,0);
810    if (NewTime.tv_sec - Time.tv_sec == 6 && NewTime.tv_usec > Time.tv_usec ||
811        NewTime.tv_sec - Time.tv_sec > 6)
812    {    
813       double Delta = NewTime.tv_sec - Time.tv_sec + 
814                      (NewTime.tv_usec - Time.tv_usec)/1000000.0;
815       
816       // Compute the CPS value
817       if (Delta < 0.01)
818          CurrentCPS = 0;
819       else
820          CurrentCPS = ((CurrentBytes - ResumeSize) - LastBytes)/Delta;
821       LastBytes = CurrentBytes - ResumeSize;
822       ElapsedTime = (unsigned long)Delta;
823       Time = NewTime;
824    }
825
826    return true;
827 }
828                                                                         /*}}}*/
829 // AcquireStatus::Start - Called when the download is started           /*{{{*/
830 // ---------------------------------------------------------------------
831 /* We just reset the counters */
832 void pkgAcquireStatus::Start()
833 {
834    gettimeofday(&Time,0);
835    gettimeofday(&StartTime,0);
836    LastBytes = 0;
837    CurrentCPS = 0;
838    CurrentBytes = 0;
839    TotalBytes = 0;
840    FetchedBytes = 0;
841    ElapsedTime = 0;
842    TotalItems = 0;
843    CurrentItems = 0;
844 }
845                                                                         /*}}}*/
846 // AcquireStatus::Stop - Finished downloading                           /*{{{*/
847 // ---------------------------------------------------------------------
848 /* This accurately computes the elapsed time and the total overall CPS. */
849 void pkgAcquireStatus::Stop()
850 {
851    // Compute the CPS and elapsed time
852    struct timeval NewTime;
853    gettimeofday(&NewTime,0);
854    
855    double Delta = NewTime.tv_sec - StartTime.tv_sec + 
856                   (NewTime.tv_usec - StartTime.tv_usec)/1000000.0;
857    
858    // Compute the CPS value
859    if (Delta < 0.01)
860       CurrentCPS = 0;
861    else
862       CurrentCPS = FetchedBytes/Delta;
863    LastBytes = CurrentBytes;
864    ElapsedTime = (unsigned int)Delta;
865 }
866                                                                         /*}}}*/
867 // AcquireStatus::Fetched - Called when a byte set has been fetched     /*{{{*/
868 // ---------------------------------------------------------------------
869 /* This is used to get accurate final transfer rate reporting. */
870 void pkgAcquireStatus::Fetched(unsigned long Size,unsigned long Resume)
871 {   
872    FetchedBytes += Size - Resume;
873 }
874
875
876                                                                         /*}}}*/
877 // AcquireStatus::Authenticate - Called to authenticate                 /*{{{*/
878 // ---------------------------------------------------------------------
879 /* This is used to fetch a username and password from the user */
880 bool pkgAcquireStatus::Authenticate(string Desc,string &User,string &Pass)
881 {
882    /* The default behavior for all clients is to refuse to authenticate
883       interactively; this preserves backwards compatibility. */
884    return false;
885 }
886                                                                         /*}}}*/
887 // vim:sts=3:sw=3